From f7897c60b5a4ca9ca045debad196c852a28ed6e2 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Tue, 10 Sep 2024 14:48:21 +0200 Subject: [PATCH 01/42] update readmes --- zmsapi/public/doc/README.md | 1 + zmscitizenapi/public/doc/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/zmsapi/public/doc/README.md b/zmsapi/public/doc/README.md index fc05e5516..a42fb1a86 100644 --- a/zmsapi/public/doc/README.md +++ b/zmsapi/public/doc/README.md @@ -14,6 +14,7 @@ Reachable at: ``` http://[::]:8001/public/doc/ https://zms.ddev.site/terminvereinbarung/api/2/doc/index.html +https://it-at-m.github.io/eappointment/zmsapi/public/doc/index.html ``` diff --git a/zmscitizenapi/public/doc/README.md b/zmscitizenapi/public/doc/README.md index 7be50aa45..3a1b5366b 100644 --- a/zmscitizenapi/public/doc/README.md +++ b/zmscitizenapi/public/doc/README.md @@ -14,6 +14,7 @@ Reachable at: ``` http://[::]:8000/public/doc/ https://zms.ddev.site/terminvereinbarung/api/citizen/doc/index.html +https://it-at-m.github.io/eappointment/zmscitizenapi/public/doc/index.html ``` From e91a517c61b3004b648034b5452956b442a05853 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Tue, 17 Sep 2024 13:38:46 +0200 Subject: [PATCH 02/42] refactor some functions to base controller class --- zmscitizenapi/VERSION | 1 - .../src/Zmscitizenapi/Application.php | 36 +++++++-- .../src/Zmscitizenapi/AppointmentGet.php | 63 +--------------- .../AvailableAppointmentsList.php | 11 --- .../src/Zmscitizenapi/AvailableDaysList.php | 10 --- .../src/Zmscitizenapi/BaseController.php | 75 +++++++++++++++++++ .../src/Zmscitizenapi/CaptchaGet.php | 10 +-- .../tests/Zmscitizenapi/CaptchaGetTest.php | 3 + 8 files changed, 115 insertions(+), 94 deletions(-) delete mode 100644 zmscitizenapi/VERSION diff --git a/zmscitizenapi/VERSION b/zmscitizenapi/VERSION deleted file mode 100644 index aaf83c3b5..000000000 --- a/zmscitizenapi/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.25.00-muc11-194-g23bcd19c2 diff --git a/zmscitizenapi/src/Zmscitizenapi/Application.php b/zmscitizenapi/src/Zmscitizenapi/Application.php index e54433be5..398088bec 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Application.php +++ b/zmscitizenapi/src/Zmscitizenapi/Application.php @@ -12,15 +12,10 @@ class Application extends \BO\Slim\Application const IDENTIFIER = 'Zmscitizenapi'; /** - * HTTP url for api + * HTTP url for API */ const ZMS_API_URL = 'http://user:pass@host.tdl'; - /** - * Flag for enabling maintenance mode - */ - const MAINTENANCE_MODE_ENABLED = false; - /** * Name of the source which should be used for the API */ @@ -34,4 +29,33 @@ class Application extends \BO\Slim\Application public static $http = null; public static $http_curl_config = []; + + /** + * Maintenance mode flag, initialized dynamically + */ + public static bool $MAINTENANCE_MODE_ENABLED; + + /** + * CAPTCHA-related settings, initialized dynamically + */ + public static bool $CAPTCHA_ENABLED; + public static string $FRIENDLYCAPTCHA_SECRET; + public static string $FRIENDLYCAPTCHA_SITEKEY; + public static string $FRIENDLYCAPTCHA_ENDPOINT; + public static string $FRIENDLYCAPTCHA_ENDPOINT_PUZZLE; + + /** + * Static initializer to set dynamic settings + */ + public static function initialize() + { + self::$MAINTENANCE_MODE_ENABLED = getenv('MAINTENANCE_ENABLED') === "0"; + self::$CAPTCHA_ENABLED = getenv('CAPTCHA_ENABLED') === "1"; + self::$FRIENDLYCAPTCHA_SECRET = getenv('FRIENDLYCAPTCHA_SECRET') ?: "xxxxxxxxx"; + self::$FRIENDLYCAPTCHA_SITEKEY = getenv('FRIENDLYCAPTCHA_SITEKEY') ?: "xxxxxxxxx"; + self::$FRIENDLYCAPTCHA_ENDPOINT = getenv('FRIENDLYCAPTCHA_ENDPOINT') ?: "https://api.friendlycaptcha.com/api/v1/siteverify"; + self::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE = getenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE') ?: "https://api.friendlycaptcha.com/api/v1/puzzle"; + } } + +Application::initialize(); \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php index b65888885..9ae6b9266 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php @@ -39,7 +39,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo } try { - $process = $this->getProcessFromZmsApi($processId, $authKey); + $process = $this->getProcessById($processId, $authKey); if (!$process) { $responseContent = [ @@ -66,66 +66,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo } } } - - protected function getProcessFromZmsApi($processId, $authKey) - { - $resolveReferences = 2; - $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ - 'resolveReferences' => $resolveReferences - ])->getEntity(); - - return $process; - } - - protected function getThinnedProcessData($myProcess) - { - if (!$myProcess || !isset($myProcess->id)) { - return []; - } - - $subRequestCounts = []; - $mainServiceId = null; - $mainServiceCount = 0; - - if (isset($myProcess->requests)) { - $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); - - if (count($requests) > 0) { - $mainServiceId = $requests[0]->id; - - foreach ($requests as $request) { - if ($request->id === $mainServiceId) { - $mainServiceCount++; - } else { - if (!isset($subRequestCounts[$request->id])) { - $subRequestCounts[$request->id] = [ - 'id' => $request->id, - 'count' => 0 - ]; - } - $subRequestCounts[$request->id]['count']++; - } - } - } - } - - return [ - 'processId' => $myProcess->id, - 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, - 'authKey' => $myProcess->authKey ?? null, - 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, - 'customTextfield' => $myProcess->customTextfield ?? null, - 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, - 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, - 'officeName' => $myProcess->scope->contact->name ?? null, - 'officeId' => $myProcess->scope->provider->id ?? null, - 'scope' => $myProcess->scope ?? null, - 'subRequestCounts' => array_values($subRequestCounts), - 'serviceId' => $mainServiceId, - 'serviceCount' => $mainServiceCount - ]; - } - + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface { $response = $response->withStatus($statusCode) diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php index a8ec27592..151d695f9 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php @@ -146,17 +146,6 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $responseContent, 500); } } - - - private function convertDateToDayMonthYear($dateString) - { - $date = new \DateTime($dateString); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y'), - ]; - } private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface { diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php index 5a3062d6e..567cef28f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php @@ -131,16 +131,6 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo } } - private function convertDateToDayMonthYear($dateString) - { - $date = new \DateTime($dateString); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y'), - ]; - } - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface { $response = $response->withStatus($statusCode) diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 0f8a668c9..4da07372d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -26,4 +26,79 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo { return parent::__invoke($request, $response, $args); } + + protected function convertDateToDayMonthYear($dateString) + { + $date = new \DateTime($dateString); + return [ + 'day' => (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y'), + ]; + } + + + + + + + + protected function getProcessById($processId, $authKey) + { + $resolveReferences = 2; + $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ + 'resolveReferences' => $resolveReferences + ])->getEntity(); + + return $process; + } + + protected function getThinnedProcessData($myProcess) + { + if (!$myProcess || !isset($myProcess->id)) { + return []; + } + + $subRequestCounts = []; + $mainServiceId = null; + $mainServiceCount = 0; + + if (isset($myProcess->requests)) { + $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); + + if (count($requests) > 0) { + $mainServiceId = $requests[0]->id; + + foreach ($requests as $request) { + if ($request->id === $mainServiceId) { + $mainServiceCount++; + } else { + if (!isset($subRequestCounts[$request->id])) { + $subRequestCounts[$request->id] = [ + 'id' => $request->id, + 'count' => 0 + ]; + } + $subRequestCounts[$request->id]['count']++; + } + } + } + } + + return [ + 'processId' => $myProcess->id, + 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, + 'authKey' => $myProcess->authKey ?? null, + 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, + 'customTextfield' => $myProcess->customTextfield ?? null, + 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, + 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, + 'officeName' => $myProcess->scope->contact->name ?? null, + 'officeId' => $myProcess->scope->provider->id ?? null, + 'scope' => $myProcess->scope ?? null, + 'subRequestCounts' => array_values($subRequestCounts), + 'serviceId' => $mainServiceId, + 'serviceCount' => $mainServiceCount + ]; + } } diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index a6ff88f6b..0231d4e1a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -10,12 +10,12 @@ class CaptchaGet extends BaseController { public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - // Retrieve captcha details from environment variables + // Retrieve captcha details from the Application class $captchaDetails = [ - 'siteKey' => getenv('FRIENDLYCAPTCHA_SITEKEY'), - 'captchaEndpoint' => getenv('FRIENDLYCAPTCHA_ENDPOINT'), - 'puzzle' => getenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE'), - 'captchaEnabled' => getenv('CAPTCHA_ENABLED') === "1" + 'siteKey' => Application::$FRIENDLYCAPTCHA_SITEKEY, + 'captchaEndpoint' => Application::$FRIENDLYCAPTCHA_ENDPOINT, + 'puzzle' => Application::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE, + 'captchaEnabled' => Application::$CAPTCHA_ENABLED ]; // Return the captcha details as JSON diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index 02fc8d417..f592448a3 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -2,6 +2,7 @@ namespace BO\Zmscitizenapi\Tests; +use BO\Zmscitizenapi\Application; use BO\Zmscitizenapi\CaptchaGet; class CaptchaGetTest extends Base @@ -16,6 +17,8 @@ public function setUp(): void putenv('FRIENDLYCAPTCHA_ENDPOINT=https://api.friendlycaptcha.com/api/v1/siteverify'); putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE=https://api.friendlycaptcha.com/api/v1/puzzle'); putenv('CAPTCHA_ENABLED=1'); + + Application::initialize(); } public function tearDown(): void From a43962e26668f7b120b7ab7cdb7f8b8e5dea4a2b Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Tue, 17 Sep 2024 13:59:01 +0200 Subject: [PATCH 03/42] add zmsapi methods to base controller --- .../src/Zmscitizenapi/BaseController.php | 135 +++++++++++++++--- .../src/Zmscitizenapi/CaptchaGet.php | 2 - 2 files changed, 119 insertions(+), 18 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 4da07372d..7bfea2c7f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -27,6 +27,8 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return parent::__invoke($request, $response, $args); } + // ----------------- Helpers --------------------- + protected function convertDateToDayMonthYear($dateString) { $date = new \DateTime($dateString); @@ -37,22 +39,6 @@ protected function convertDateToDayMonthYear($dateString) ]; } - - - - - - - protected function getProcessById($processId, $authKey) - { - $resolveReferences = 2; - $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ - 'resolveReferences' => $resolveReferences - ])->getEntity(); - - return $process; - } - protected function getThinnedProcessData($myProcess) { if (!$myProcess || !isset($myProcess->id)) { @@ -101,4 +87,121 @@ protected function getThinnedProcessData($myProcess) 'serviceCount' => $mainServiceCount ]; } + + + + // ----------------- ZmsApiClient --------------------- + + protected function getSources() + { + $requestUrl = '/source/dldb/?resolveReferences=2'; + + return \App::$http->readGetResult($requestUrl)->getEntity(); + } + + protected function getScopes() + { + $sources = $this->getSources(); + return $sources->scopes; + } + + protected function getFreeDays($providers, $requests, $firstDay, $lastDay) + { + $requestUrl = '/calendar/'; + $dataPayload = [ + 'firstDay' => $firstDay, + 'lastDay' => $lastDay, + 'providers' => $providers, + 'requests' => $requests, + ]; + + return \App::$http->readPostResult($requestUrl, $dataPayload)->getEntity(); + } + + protected function getFreeTimeslots($providers, $requests, $firstDay, $lastDay) + { + $requestUrl = '/process/status/free/'; + $dataPayload = [ + 'firstDay' => $firstDay, + 'lastDay' => $lastDay, + 'providers' => $providers, + 'requests' => $requests, + ]; + + return \App::$http->readPostResult($requestUrl, $dataPayload)->getEntity(); + } + + protected function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) + { + $requests = []; + + foreach ($serviceIds as $index => $serviceId) { + $count = intval($serviceCounts[$index]); + for ($i = 0; $i < $count; $i++) { + $requests[] = [ + 'id' => $serviceId, + 'source' => 'dldb' + ]; + } + } + + $appointmentProcess['requests'] = $requests; + + return \App::$http->readPostResult('/process/status/reserved/', $appointmentProcess)->getEntity(); + } + + protected function submitClientData($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + protected function preconfirmProcess($process) + { + $url = '/process/status/preconfirmed/'; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + protected function confirmProcess($process) + { + $url = '/process/status/confirmed/'; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + protected function cancelAppointment($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return \App::$http->readDeleteResult($url, $process)->getEntity(); + } + + protected function sendConfirmationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + protected function sendPreconfirmationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + protected function sendCancelationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + + protected function getProcessById($processId, $authKey) + { + $resolveReferences = 2; + $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ + 'resolveReferences' => $resolveReferences + ])->getEntity(); + + return $process; + } + + } diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index 0231d4e1a..99c49e63e 100644 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -10,7 +10,6 @@ class CaptchaGet extends BaseController { public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - // Retrieve captcha details from the Application class $captchaDetails = [ 'siteKey' => Application::$FRIENDLYCAPTCHA_SITEKEY, 'captchaEndpoint' => Application::$FRIENDLYCAPTCHA_ENDPOINT, @@ -18,7 +17,6 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo 'captchaEnabled' => Application::$CAPTCHA_ENABLED ]; - // Return the captcha details as JSON return Render::withJson($response, $captchaDetails); } } From 48da06a1bc81986ec2b4f1f804ed46a875c986cb Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Tue, 17 Sep 2024 14:31:36 +0200 Subject: [PATCH 04/42] add helper methods to base controller --- .../AvailableAppointmentsList.php | 4 ++-- .../src/Zmscitizenapi/AvailableDaysList.php | 4 ++-- .../src/Zmscitizenapi/BaseController.php | 23 ++++++++++++++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php index 151d695f9..6167aa36b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php @@ -61,8 +61,8 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo try { $calendar = new CalendarEntity(); - $calendar->firstDay = $this->convertDateToDayMonthYear($date); - $calendar->lastDay = $this->convertDateToDayMonthYear($date); + $calendar->firstDay = $this->getInternalDateFromISO($date); + $calendar->lastDay = $this->getInternalDateFromISO($date); $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; $calendar->requests = []; diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php index 567cef28f..d010edde0 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php @@ -66,8 +66,8 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo if (empty($errors)) { try { - $firstDay = $this->convertDateToDayMonthYear($startDate); - $lastDay = $this->convertDateToDayMonthYear($endDate); + $firstDay = $this->getInternalDateFromISO($startDate); + $lastDay = $this->getInternalDateFromISO($endDate); $calendar = new CalendarEntity(); $calendar->firstDay = $firstDay; diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 7bfea2c7f..c3d89269d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -29,7 +29,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo // ----------------- Helpers --------------------- - protected function convertDateToDayMonthYear($dateString) + protected function getInternalDateFromISO($dateString) { $date = new \DateTime($dateString); return [ @@ -39,6 +39,21 @@ protected function convertDateToDayMonthYear($dateString) ]; } + protected function getInternalDateFromTimestamp(int $timestamp) + { + $date = (new \DateTime())->setTimestamp($timestamp); + return [ + 'day' => (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y') + ]; + } + + protected function onlyUniqueAppointments($value, $index, $self) + { + return array_search($value, $self) === $index; + } + protected function getThinnedProcessData($myProcess) { if (!$myProcess || !isset($myProcess->id)) { @@ -51,10 +66,8 @@ protected function getThinnedProcessData($myProcess) if (isset($myProcess->requests)) { $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); - if (count($requests) > 0) { $mainServiceId = $requests[0]->id; - foreach ($requests as $request) { if ($request->id === $mainServiceId) { $mainServiceCount++; @@ -88,6 +101,10 @@ protected function getThinnedProcessData($myProcess) ]; } + protected function uniqueElementsFilter($value, $index, $self) + { + return array_search($value, $self) === $index; + } // ----------------- ZmsApiClient --------------------- From d64c675a9bf8a9e19199b978721a512e6f9ae54f Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 18 Sep 2024 11:29:58 +0200 Subject: [PATCH 05/42] refactored into services and controllers --- zmscitizenapi/routing.php | 32 ++-- .../src/Zmscitizenapi/AppointmentGet.php | 77 --------- .../AvailableAppointmentsList.php | 157 ------------------ .../src/Zmscitizenapi/AvailableDaysList.php | 141 ---------------- .../src/Zmscitizenapi/BaseController.php | 122 +------------- .../src/Zmscitizenapi/CaptchaGet.php | 22 --- .../{ => Controllers}/AppointmentCancel.php | 3 +- .../{ => Controllers}/AppointmentConfirm.php | 3 +- .../Controllers/AppointmentGet.php | 39 +++++ .../AppointmentPreconfirm.php | 3 +- .../{ => Controllers}/AppointmentReserve.php | 3 +- .../{ => Controllers}/AppointmentUpdate.php | 3 +- .../Controllers/AvailableAppointmentsList.php | 35 ++++ .../Controllers/AvailableDaysList.php | 35 ++++ .../Zmscitizenapi/Controllers/CaptchaGet.php | 27 +++ .../Controllers/OfficesByServiceList.php | 40 +++++ .../Zmscitizenapi/Controllers/OfficesList.php | 25 +++ .../Controllers/OfficesServicesRelations.php | 30 ++++ .../Controllers/ScopeByIdGet.php | 40 +++++ .../Zmscitizenapi/Controllers/ScopesList.php | 25 +++ .../Controllers/ServicesByOfficeList.php | 39 +++++ .../Controllers/ServicesList.php | 25 +++ .../Zmscitizenapi/OfficesByServiceList.php | 84 ---------- .../src/Zmscitizenapi/OfficesList.php | 54 ------ .../src/Zmscitizenapi/ScopesList.php | 38 ----- .../Services/AppointmentService.php | 122 ++++++++++++++ .../Services/AvailableAppointmentsService.php | 152 +++++++++++++++++ .../Services/AvailableDaysService.php | 106 ++++++++++++ .../Zmscitizenapi/Services/CaptchaService.php | 18 ++ .../Zmscitizenapi/Services/OfficesService.php | 118 +++++++++++++ .../OfficesServicesRelationsService.php} | 72 ++++---- .../Zmscitizenapi/Services/ProcessService.php | 110 ++++++++++++ .../ScopesService.php} | 64 ++++--- .../ServicesService.php} | 55 +++--- .../src/Zmscitizenapi/ServicesList.php | 33 ---- .../Zmscitizenapi/AppointmentCancelTest.php | 2 +- .../Zmscitizenapi/AppointmentConfirmTest.php | 2 +- .../Zmscitizenapi/AppointmentGetTest.php | 2 +- .../AppointmentPreconfirmTest.php | 2 +- .../Zmscitizenapi/AppointmentReserveTest.php | 2 +- .../Zmscitizenapi/AppointmentUpdateTest.php | 2 +- .../AvailableAppointmentsListTest.php | 2 +- .../Zmscitizenapi/AvailableDaysListTest.php | 2 +- .../tests/Zmscitizenapi/CaptchaGetTest.php | 2 +- .../OfficesByServiceListTest.php | 4 +- .../tests/Zmscitizenapi/OfficesListTest.php | 8 +- .../OfficesServicesRelationsTest.php | 8 +- .../tests/Zmscitizenapi/ScopeByIdGetTest.php | 4 +- .../tests/Zmscitizenapi/ScopesListTest.php | 2 +- .../ServicesByOfficeListTest.php | 4 +- .../tests/Zmscitizenapi/ServicesListTest.php | 2 +- zmsentities/package-lock.json | 14 ++ 52 files changed, 1170 insertions(+), 846 deletions(-) delete mode 100644 zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php rename zmscitizenapi/src/Zmscitizenapi/{ => Controllers}/AppointmentCancel.php (80%) rename zmscitizenapi/src/Zmscitizenapi/{ => Controllers}/AppointmentConfirm.php (80%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentGet.php rename zmscitizenapi/src/Zmscitizenapi/{ => Controllers}/AppointmentPreconfirm.php (80%) rename zmscitizenapi/src/Zmscitizenapi/{ => Controllers}/AppointmentReserve.php (80%) rename zmscitizenapi/src/Zmscitizenapi/{ => Controllers}/AppointmentUpdate.php (80%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableAppointmentsList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/OfficesList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/ScopesList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php rename zmscitizenapi/src/Zmscitizenapi/{OfficesServicesRelations.php => Services/OfficesServicesRelationsService.php} (78%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php rename zmscitizenapi/src/Zmscitizenapi/{ScopeByIdGet.php => Services/ScopesService.php} (59%) rename zmscitizenapi/src/Zmscitizenapi/{ServicesByOfficeList.php => Services/ServicesService.php} (63%) delete mode 100644 zmscitizenapi/src/Zmscitizenapi/ServicesList.php create mode 100644 zmsentities/package-lock.json diff --git a/zmscitizenapi/routing.php b/zmscitizenapi/routing.php index 5060cb8e6..d95628c82 100644 --- a/zmscitizenapi/routing.php +++ b/zmscitizenapi/routing.php @@ -24,7 +24,7 @@ */ \App::$slim->get( '/services/', - '\BO\Zmscitizenapi\ServicesList' + '\BO\Zmscitizenapi\Controllers\ServicesList' ) ->setName("ServicesList"); @@ -48,7 +48,7 @@ */ \App::$slim->get( '/scopes/', - '\BO\Zmscitizenapi\ScopesList' + '\BO\Zmscitizenapi\Controllers\ScopesList' ) ->setName("ScopesList"); @@ -72,7 +72,7 @@ */ \App::$slim->get( '/offices/', - '\BO\Zmscitizenapi\OfficesList' + '\BO\Zmscitizenapi\Controllers\OfficesList' ) ->setName("OfficesList"); @@ -96,7 +96,7 @@ */ \App::$slim->get( '/offices-and-services/', - '\BO\Zmscitizenapi\OfficesServicesRelations' + '\BO\Zmscitizenapi\Controllers\OfficesServicesRelations' ) ->setName("OfficesServicesRelations"); @@ -128,7 +128,7 @@ */ \App::$slim->get( '/scope-by-id/', - '\BO\Zmscitizenapi\ScopeByIdGet' + '\BO\Zmscitizenapi\Controllers\ScopeByIdGet' ) ->setName("ScopeByIdGet"); @@ -158,7 +158,7 @@ */ \App::$slim->get( '/services-by-office/', - '\BO\Zmscitizenapi\ServicesByOfficeList' + '\BO\Zmscitizenapi\Controllers\ServicesByOfficeList' ) ->setName("ServicesByOfficeList"); @@ -188,7 +188,7 @@ */ \App::$slim->get( '/offices-by-service/', - '\BO\Zmscitizenapi\OfficesByServiceList' + '\BO\Zmscitizenapi\Controllers\OfficesByServiceList' ) ->setName("OfficesByServiceList"); @@ -223,7 +223,7 @@ */ \App::$slim->get( '/available-days/', - '\BO\Zmscitizenapi\AvailableDaysList' + '\BO\Zmscitizenapi\Controllers\AvailableDaysList' ) ->setName("AvailableDaysList"); @@ -263,7 +263,7 @@ */ \App::$slim->get( '/available-appointments/', - '\BO\Zmscitizenapi\AvailableAppointmentsList' + '\BO\Zmscitizenapi\Controllers\AvailableAppointmentsList' ) ->setName("AvailableAppointmentsList"); @@ -318,7 +318,7 @@ */ \App::$slim->get( '/appointment/', - '\BO\Zmscitizenapi\AppointmentGet' + '\BO\Zmscitizenapi\Controllers\AppointmentGet' ) ->setName("AppointmentGet"); @@ -342,7 +342,7 @@ */ \App::$slim->get( '/captcha-details/', - '\BO\Zmscitizenapi\CaptchaGet' + '\BO\Zmscitizenapi\Controllers\CaptchaGet' ) ->setName("CaptchaGet"); @@ -393,7 +393,7 @@ */ \App::$slim->post( '/reserve-appointment/', - '\BO\Zmscitizenapi\AppointmentReserve' + '\BO\Zmscitizenapi\Controllers\AppointmentReserve' ) ->setName("AppointmentReserve"); @@ -444,7 +444,7 @@ */ \App::$slim->post( '/update-appointment/', - '\BO\Zmscitizenapi\AppointmentUpdate' + '\BO\Zmscitizenapi\Controllers\AppointmentUpdate' ) ->setName("AppointmentUpdate"); @@ -495,7 +495,7 @@ */ \App::$slim->post( '/confirm-appointment/', - '\BO\Zmscitizenapi\AppointmentConfirm' + '\BO\Zmscitizenapi\Controllers\AppointmentConfirm' ) ->setName("AppointmentConfirm"); @@ -546,7 +546,7 @@ */ \App::$slim->post( '/preconfirm-appointment/', - '\BO\Zmscitizenapi\AppointmentPreconfirm' + '\BO\Zmscitizenapi\Controllers\AppointmentPreconfirm' ) ->setName("AppointmentPreconfirm"); @@ -597,6 +597,6 @@ */ \App::$slim->post( '/cancel-appointment/', - '\BO\Zmscitizenapi\AppointmentCancel' + '\BO\Zmscitizenapi\Controllers\AppointmentCancel' ) ->setName("AppointmentCancel"); diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php deleted file mode 100644 index 9ae6b9266..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php +++ /dev/null @@ -1,77 +0,0 @@ -getQueryParams()['processId'] ?? null; - $authKey = $request->getQueryParams()['authKey'] ?? null; - - $errors = []; - - if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'processId should be a 32-bit integer', - 'path' => 'processId', - 'location' => 'query' - ]; - } - - if (!$authKey || !is_string($authKey)) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'authKey should be a string', - 'path' => 'authKey', - 'location' => 'query' - ]; - } - - if (!empty($errors)) { - $responseContent = ['errors' => $errors]; - return $this->createJsonResponse($response, $responseContent, 400); - } - - try { - $process = $this->getProcessById($processId, $authKey); - - if (!$process) { - $responseContent = [ - 'errorMessage' => 'Termin wurde nicht gefunden', - ]; - return $this->createJsonResponse($response, $responseContent, 404); - } - - $responseData = $this->getThinnedProcessData($process); - - return $this->createJsonResponse($response, $responseData, 200); - - } catch (\Exception $e) { - if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { - $responseContent = [ - 'errorMessage' => 'Termin wurde nicht gefunden', - ]; - return $this->createJsonResponse($response, $responseContent, 404); - } else { - $responseContent = [ - 'error' => 'Unexpected error: ' . $e->getMessage() - ]; - return $this->createJsonResponse($response, $responseContent, 500); - } - } - } - - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); - return $response; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php deleted file mode 100644 index 6167aa36b..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php +++ /dev/null @@ -1,157 +0,0 @@ -getQueryParams(); - $date = $queryParams['date'] ?? null; - $officeId = $queryParams['officeId'] ?? null; - $serviceIds = isset($queryParams['serviceId']) ? explode(',', $queryParams['serviceId']) : []; - $serviceCounts = isset($queryParams['serviceCount']) ? explode(',', $queryParams['serviceCount']) : []; - - $errors = []; - - if (!$date) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'date is required and must be a valid date', - 'path' => 'date', - 'location' => 'body' - ]; - } - - if (!$officeId || !is_numeric($officeId)) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'officeId should be a 32-bit integer', - 'path' => 'officeId', - 'location' => 'body' - ]; - } - - if (empty($serviceIds[0]) || !preg_match('/^\d+(,\d+)*$/', $queryParams['serviceId'] ?? '')) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'serviceId should be a comma-separated string of integers', - 'path' => 'serviceId', - 'location' => 'body' - ]; - } - - if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', $queryParams['serviceCount'] ?? '')) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'serviceCount should be a comma-separated string of integers', - 'path' => 'serviceCount', - 'location' => 'body' - ]; - } - if (!empty($errors)) { - $responseContent = ['errors' => $errors]; - return $this->createJsonResponse($response, $responseContent, 400); - } - - try { - $calendar = new CalendarEntity(); - $calendar->firstDay = $this->getInternalDateFromISO($date); - $calendar->lastDay = $this->getInternalDateFromISO($date); - $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; - - $calendar->requests = []; - foreach ($serviceIds as $index => $serviceId) { - $slotCount = isset($serviceCounts[$index]) ? intval($serviceCounts[$index]) : 1; - for ($i = 0; $i < $slotCount; $i++) { - $calendar->requests[] = [ - 'id' => $serviceId, - 'source' => 'dldb', - 'slotCount' => 1 - ]; - } - } - - try { - $freeSlots = \App::$http->readPostResult('/process/status/free/', $calendar); - if (!$freeSlots || !method_exists($freeSlots, 'getCollection')) { - throw new \Exception('Invalid response from API'); - } - $freeSlots = $freeSlots->getCollection(); - } catch (\Exception $e) { - error_log('Error in AvailableAppointmentsList during API request: ' . $e->getMessage()); - $responseContent = [ - 'appointmentTimestamps' => [], - 'errorCode' => 'appointmentNotAvailable', - 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', - 'lastModified' => round(microtime(true) * 1000) - ]; - return $this->createJsonResponse($response, $responseContent, 500); - } - - if (empty($freeSlots)) { - $responseContent = [ - 'appointmentTimestamps' => [], - 'errorCode' => 'appointmentNotAvailable', - 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', - 'lastModified' => round(microtime(true) * 1000) - ]; - return $this->createJsonResponse($response, $responseContent, 404); - } - - $currentTimestamp = time(); - - $appointmentTimestamps = []; - foreach ($freeSlots as $slot) { - foreach ($slot->appointments as $appointment) { - $timestamp = (int)$appointment->date; - - if (!in_array($timestamp, $appointmentTimestamps) && $timestamp > $currentTimestamp) { - $appointmentTimestamps[] = $timestamp; - } - } - } - - if (empty($appointmentTimestamps)) { - $responseContent = [ - 'appointmentTimestamps' => [], - 'errorCode' => 'appointmentNotAvailable', - 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', - 'lastModified' => round(microtime(true) * 1000) - ]; - return $this->createJsonResponse($response, $responseContent, 404); - } - - sort($appointmentTimestamps); - - $responseContent = [ - 'appointmentTimestamps' => $appointmentTimestamps, - 'lastModified' => round(microtime(true) * 1000) - ]; - return $this->createJsonResponse($response, $responseContent, 200); - - } catch (\Exception $e) { - error_log('Error in AvailableAppointmentsList: ' . $e->getMessage()); - $responseContent = [ - 'appointmentTimestamps' => [], - 'errorCode' => 'noAppointmentForThisScope', - 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine', - 'lastModified' => round(microtime(true) * 1000) - ]; - return $this->createJsonResponse($response, $responseContent, 500); - } - } - - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content, JSON_NUMERIC_CHECK)); - return $response; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php deleted file mode 100644 index d010edde0..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php +++ /dev/null @@ -1,141 +0,0 @@ -getQueryParams(); - $officeId = $queryParams['officeId'] ?? null; - $serviceId = $queryParams['serviceId'] ?? null; - $serviceCounts = isset($queryParams['serviceCount']) ? explode(',', $queryParams['serviceCount']) : []; - $startDate = $queryParams['startDate'] ?? null; - $endDate = $queryParams['endDate'] ?? null; - - $errors = []; - - if (!$startDate) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'startDate is required and must be a valid date', - 'path' => 'startDate', - 'location' => 'query' - ]; - } - - if (!$endDate) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'endDate is required and must be a valid date', - 'path' => 'endDate', - 'location' => 'query' - ]; - } - - if (!$officeId || !is_numeric($officeId)) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'officeId should be a 32-bit integer', - 'path' => 'officeId', - 'location' => 'query' - ]; - } - - if (!$serviceId || !is_numeric($serviceId)) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'serviceId should be a 32-bit integer', - 'path' => 'serviceId', - 'location' => 'query' - ]; - } - - if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', $queryParams['serviceCount'] ?? '')) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'serviceCount should be a comma-separated string of integers', - 'path' => 'serviceCount', - 'location' => 'body' - ]; - } - - if (empty($errors)) { - try { - $firstDay = $this->getInternalDateFromISO($startDate); - $lastDay = $this->getInternalDateFromISO($endDate); - - $calendar = new CalendarEntity(); - $calendar->firstDay = $firstDay; - $calendar->lastDay = $lastDay; - $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; - $calendar->requests = [ - [ - 'id' => $serviceId, - 'source' => 'dldb', - 'slotCount' => $serviceCounts - ] - ]; - - try { - $apiResponse = \App::$http->readPostResult('/calendar/', $calendar); - } catch (\Exception $e) { - error_log('Error in AvailableDaysList during API request: ' . $e->getMessage()); - $responseContent = [ - 'availableDays' => [], - 'errorCode' => 'apiRequestFailed', - 'errorMessage' => 'API request failed: ' . $e->getMessage() - ]; - return $this->createJsonResponse($response, $responseContent, 500); - } - - $calendarEntity = $apiResponse->getEntity(); - $daysCollection = $calendarEntity->days; - $formattedDays = []; - foreach ($daysCollection as $day) { - $formattedDays[] = sprintf('%04d-%02d-%02d', $day->year, $day->month, $day->day); - } - - if (empty($formattedDays)) { - $responseContent = [ - 'availableDays' => [], - 'errorCode' => 'noAppointmentForThisScope', - 'errorMessage' => 'No available days found for the given criteria', - ]; - return $this->createJsonResponse($response, $responseContent, 404); - } - - $responseContent = [ - 'availableDays' => $formattedDays, - 'lastModified' => round(microtime(true) * 1000), - ]; - return $this->createJsonResponse($response, $responseContent, 200); - - } catch (\Exception $e) { - error_log('Error in AvailableDaysList: ' . $e->getMessage()); - $responseContent = [ - 'availableDays' => [], - 'errorCode' => 'internalError', - 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine', - 'lastModified' => round(microtime(true) * 1000) - ]; - return $this->createJsonResponse($response, $responseContent, 500); - } - } else { - $responseContent = ['errors' => $errors]; - return $this->createJsonResponse($response, $responseContent, 400); - } - } - - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content, JSON_NUMERIC_CHECK)); - return $response; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index c3d89269d..55e94fa31 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -5,10 +5,6 @@ use \Psr\Http\Message\RequestInterface; use \Psr\Http\Message\ResponseInterface; -/** - * @SuppressWarnings(NumberOfChildren) - * - */ abstract class BaseController extends \BO\Slim\Controller { public function __invoke(RequestInterface $request, ResponseInterface $response, array $args) @@ -105,120 +101,6 @@ protected function uniqueElementsFilter($value, $index, $self) { return array_search($value, $self) === $index; } + - - // ----------------- ZmsApiClient --------------------- - - protected function getSources() - { - $requestUrl = '/source/dldb/?resolveReferences=2'; - - return \App::$http->readGetResult($requestUrl)->getEntity(); - } - - protected function getScopes() - { - $sources = $this->getSources(); - return $sources->scopes; - } - - protected function getFreeDays($providers, $requests, $firstDay, $lastDay) - { - $requestUrl = '/calendar/'; - $dataPayload = [ - 'firstDay' => $firstDay, - 'lastDay' => $lastDay, - 'providers' => $providers, - 'requests' => $requests, - ]; - - return \App::$http->readPostResult($requestUrl, $dataPayload)->getEntity(); - } - - protected function getFreeTimeslots($providers, $requests, $firstDay, $lastDay) - { - $requestUrl = '/process/status/free/'; - $dataPayload = [ - 'firstDay' => $firstDay, - 'lastDay' => $lastDay, - 'providers' => $providers, - 'requests' => $requests, - ]; - - return \App::$http->readPostResult($requestUrl, $dataPayload)->getEntity(); - } - - protected function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) - { - $requests = []; - - foreach ($serviceIds as $index => $serviceId) { - $count = intval($serviceCounts[$index]); - for ($i = 0; $i < $count; $i++) { - $requests[] = [ - 'id' => $serviceId, - 'source' => 'dldb' - ]; - } - } - - $appointmentProcess['requests'] = $requests; - - return \App::$http->readPostResult('/process/status/reserved/', $appointmentProcess)->getEntity(); - } - - protected function submitClientData($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/"; - return \App::$http->readPostResult($url, $process)->getEntity(); - } - - protected function preconfirmProcess($process) - { - $url = '/process/status/preconfirmed/'; - return \App::$http->readPostResult($url, $process)->getEntity(); - } - - protected function confirmProcess($process) - { - $url = '/process/status/confirmed/'; - return \App::$http->readPostResult($url, $process)->getEntity(); - } - - protected function cancelAppointment($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/"; - return \App::$http->readDeleteResult($url, $process)->getEntity(); - } - - protected function sendConfirmationEmail($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; - return \App::$http->readPostResult($url, $process)->getEntity(); - } - - protected function sendPreconfirmationEmail($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; - return \App::$http->readPostResult($url, $process)->getEntity(); - } - - protected function sendCancelationEmail($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; - return \App::$http->readPostResult($url, $process)->getEntity(); - } - - - protected function getProcessById($processId, $authKey) - { - $resolveReferences = 2; - $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ - 'resolveReferences' => $resolveReferences - ])->getEntity(); - - return $process; - } - - -} +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php deleted file mode 100644 index 99c49e63e..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ /dev/null @@ -1,22 +0,0 @@ - Application::$FRIENDLYCAPTCHA_SITEKEY, - 'captchaEndpoint' => Application::$FRIENDLYCAPTCHA_ENDPOINT, - 'puzzle' => Application::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE, - 'captchaEnabled' => Application::$CAPTCHA_ENABLED - ]; - - return Render::withJson($response, $captchaDetails); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php similarity index 80% rename from zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php rename to zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php index 7538d7495..fe7292395 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php @@ -1,7 +1,8 @@ appointmentService = new AppointmentService($processService); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $queryParams = $request->getQueryParams(); + $processId = $queryParams['processId'] ?? null; + $authKey = $queryParams['authKey'] ?? null; + + $result = $this->appointmentService->getAppointmentById($processId, $authKey); + + return $this->createJsonResponse($response, $result['data'] ?? $result, $result['status']); + } + + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content)); + return $response; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentPreconfirm.php similarity index 80% rename from zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php rename to zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentPreconfirm.php index 85534a6e5..03868f26c 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentPreconfirm.php @@ -1,7 +1,8 @@ availableAppointmentsService = new AvailableAppointmentsService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $queryParams = $request->getQueryParams(); + + $result = $this->availableAppointmentsService->getAvailableAppointments($queryParams); + + return $this->createJsonResponse($response, $result, $result['status']); + } + + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content, JSON_NUMERIC_CHECK)); + return $response; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php new file mode 100644 index 000000000..9719e387d --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php @@ -0,0 +1,35 @@ +availableDaysService = new AvailableDaysService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $queryParams = $request->getQueryParams(); + + $result = $this->availableDaysService->getAvailableDays($queryParams); + + return $this->createJsonResponse($response, $result, $result['status']); + } + + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content, JSON_NUMERIC_CHECK)); + return $response; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php new file mode 100644 index 000000000..e371742f4 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php @@ -0,0 +1,27 @@ +captchaService = new CaptchaService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $captchaDetails = $this->captchaService->getCaptchaDetails(); + + return Render::withJson($response, $captchaDetails); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php new file mode 100644 index 000000000..d04c82e77 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php @@ -0,0 +1,40 @@ +officesService = new OfficesService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $serviceIds = explode(',', $request->getQueryParams()['serviceId'] ?? ''); + + $result = $this->officesService->getOfficesByServiceIds($serviceIds); + + if (isset($result['error'])) { + return $this->createJsonResponse($response, $result, $result['status']); + } + + return $this->createJsonResponse($response, $result['offices'], $result['status']); + } + + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content)); + return $response; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php new file mode 100644 index 000000000..c19bf596c --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php @@ -0,0 +1,25 @@ +officesService = new OfficesService(); // No container + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $offices = $this->officesService->getOffices(); + return Render::withJson($response, ["offices" => $offices]); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php new file mode 100644 index 000000000..98c05becb --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php @@ -0,0 +1,30 @@ +officesServicesRelationsService = new OfficesServicesRelationsService(); // No container + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $data = $this->officesServicesRelationsService->getOfficesServicesRelations($sources); + + return Render::withJson($response, $data); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php new file mode 100644 index 000000000..33faf56c0 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php @@ -0,0 +1,40 @@ +scopesService = new ScopesService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $scopeIds = explode(',', $request->getQueryParams()['scopeId'] ?? ''); + + $result = $this->scopesService->getScopeByIds($scopeIds); + + if (isset($result['error'])) { + return $this->createJsonResponse($response, $result, $result['status']); + } + + return $this->createJsonResponse($response, $result['scopes'], $result['status']); + } + + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content)); + return $response; + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php new file mode 100644 index 000000000..b522923e8 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php @@ -0,0 +1,25 @@ +scopesService = new ScopesService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $scopes = $this->scopesService->getScopes(); + return Render::withJson($response, ["scopes" => $scopes]); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php new file mode 100644 index 000000000..654217169 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php @@ -0,0 +1,39 @@ +servicesService = new ServicesService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $officeIds = explode(',', $request->getQueryParams()['officeId'] ?? ''); + + $result = $this->servicesService->getServicesByOfficeIds($officeIds); + + if (isset($result['error'])) { + return $this->createJsonResponse($response, $result, $result['status']); + } + + return $this->createJsonResponse($response, $result['services'], $result['status']); + } + + private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content)); + return $response; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php new file mode 100644 index 000000000..dd34da105 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php @@ -0,0 +1,25 @@ +servicesService = new ServicesService(); + } + + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + { + $services = $this->servicesService->getServices(); + return Render::withJson($response, ["services" => $services]); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php deleted file mode 100644 index 62fa82ebe..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php +++ /dev/null @@ -1,84 +0,0 @@ -getQueryParams()['serviceId'] ?? ''); - $serviceIds = array_unique($serviceIds); - - if (empty($serviceIds) || $serviceIds == ['']) { - $responseContent = [ - 'offices' => [], - 'error' => 'Invalid serviceId(s)', - ]; - return $this->createJsonResponse($response, $responseContent, 400); - } - - $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $providerList = $sources->getProviderList(); - $requestRelationList = $sources->getRequestRelationList(); - - $offices = []; - $notFoundIds = []; - $addedOfficeIds = []; - - foreach ($serviceIds as $serviceId) { - $found = false; - foreach ($requestRelationList as $relation) { - if ($relation->request->id == $serviceId) { - if (!in_array($relation->provider->id, $addedOfficeIds)) { - foreach ($providerList as $provider) { - if ($provider->id == $relation->provider->id) { - $offices[] = [ - "id" => $provider->id, - "name" => $provider->name, - ]; - $addedOfficeIds[] = $provider->id; - $found = true; - break; - } - } - } else { - $found = true; - } - } - } - if (!$found) { - $notFoundIds[] = $serviceId; - } - } - - if (empty($offices)) { - $responseContent = [ - 'offices' => [], - 'error' => 'Office(s) not found for the provided serviceId(s)', - ]; - return $this->createJsonResponse($response, $responseContent, 404); - } - - $responseContent = ['offices' => $offices]; - if (!empty($notFoundIds)) { - $responseContent['warning'] = 'The following serviceId(s) were not found: ' . implode(', ', $notFoundIds); - } - - return $this->createJsonResponse($response, $responseContent, 200); - } - - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); - return $response; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php deleted file mode 100644 index 1578684b1..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php +++ /dev/null @@ -1,54 +0,0 @@ -readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $scopeList = $sources->getScopeList() ?? []; - $providerProjectionList = []; - - foreach ($sources->getProviderList() as $provider) { - $matchingScope = null; - foreach ($scopeList as $scope) { - if ($scope->provider->id == $provider->id) { - $matchingScope = $scope; - break; - } - } - $providerData = [ - "id" => $provider->id, - "name" => $provider->displayName ?? $provider->name, - ]; - if ($matchingScope) { - $providerData["scope"] = [ - "id" => $matchingScope->id, - "provider" => $matchingScope->provider, - "shortName" => $matchingScope->shortName, - "telephoneActivated" => $matchingScope->getTelephoneActivated(), - "telephoneRequired" => $matchingScope->getTelephoneRequired(), - "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired() - ]; - } - - $providerProjectionList[] = $providerData; - } - - return Render::withJson($response, [ - "offices" => $providerProjectionList, - ]); - } - -} diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php deleted file mode 100644 index 3322bd59a..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php +++ /dev/null @@ -1,38 +0,0 @@ -readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $scopeList = $sources->getScopeList() ?? []; - $scopesProjectionList = []; - - foreach ($scopeList as $scope) { - $scopesProjectionList[] = [ - "id" => $scope->id, - "provider" => $scope->provider, - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() - ]; - } - - return Render::withJson($response, [ - "scopes" => $scopesProjectionList, - ]); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php new file mode 100644 index 000000000..1cb59ea43 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php @@ -0,0 +1,122 @@ +processService = $processService; + } + + public function getAppointmentById($processId, $authKey) + { + $errors = $this->validateInputs($processId, $authKey); + if (!empty($errors)) { + return ['errors' => $errors, 'status' => 400]; + } + + try { + $process = $this->processService->getProcessById($processId, $authKey); + + if (!$process) { + return [ + 'errorMessage' => 'Termin wurde nicht gefunden', + 'status' => 404, + ]; + } + + $responseData = $this->getThinnedProcessData($process); + return ['data' => $responseData, 'status' => 200]; + + } catch (\Exception $e) { + if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { + return [ + 'errorMessage' => 'Termin wurde nicht gefunden', + 'status' => 404, + ]; + } else { + return [ + 'error' => 'Unexpected error: ' . $e->getMessage(), + 'status' => 500, + ]; + } + } + } + + private function validateInputs($processId, $authKey) + { + $errors = []; + + if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { + $errors[] = [ + 'type' => 'field', + 'msg' => 'processId should be a 32-bit integer', + 'path' => 'processId', + 'location' => 'query' + ]; + } + + if (!$authKey || !is_string($authKey)) { + $errors[] = [ + 'type' => 'field', + 'msg' => 'authKey should be a string', + 'path' => 'authKey', + 'location' => 'query' + ]; + } + + return $errors; + } + + private function getThinnedProcessData($myProcess) + { + if (!$myProcess || !isset($myProcess->id)) { + return []; + } + + $subRequestCounts = []; + $mainServiceId = null; + $mainServiceCount = 0; + + if (isset($myProcess->requests)) { + $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); + if (count($requests) > 0) { + $mainServiceId = $requests[0]->id; + foreach ($requests as $request) { + if ($request->id === $mainServiceId) { + $mainServiceCount++; + } else { + if (!isset($subRequestCounts[$request->id])) { + $subRequestCounts[$request->id] = [ + 'id' => $request->id, + 'count' => 0, + ]; + } + $subRequestCounts[$request->id]['count']++; + } + } + } + } + + return [ + 'processId' => $myProcess->id, + 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, + 'authKey' => $myProcess->authKey ?? null, + 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, + 'customTextfield' => $myProcess->customTextfield ?? null, + 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, + 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, + 'officeName' => $myProcess->scope->contact->name ?? null, + 'officeId' => $myProcess->scope->provider->id ?? null, + 'scope' => $myProcess->scope ?? null, + 'subRequestCounts' => array_values($subRequestCounts), + 'serviceId' => $mainServiceId, + 'serviceCount' => $mainServiceCount, + ]; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php new file mode 100644 index 000000000..569519774 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php @@ -0,0 +1,152 @@ +validateQueryParams($date, $officeId, $serviceIds, $serviceCounts); + + if (!empty($errors)) { + return ['errors' => $errors, 'status' => 400]; + } + + try { + $calendar = new CalendarEntity(); + $calendar->firstDay = $this->getInternalDateFromISO($date); + $calendar->lastDay = $this->getInternalDateFromISO($date); + $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; + + $calendar->requests = []; + foreach ($serviceIds as $index => $serviceId) { + $slotCount = isset($serviceCounts[$index]) ? intval($serviceCounts[$index]) : 1; + for ($i = 0; $i < $slotCount; $i++) { + $calendar->requests[] = [ + 'id' => $serviceId, + 'source' => 'dldb', + 'slotCount' => 1, + ]; + } + } + + $freeSlots = \App::$http->readPostResult('/process/status/free/', $calendar); + if (!$freeSlots || !method_exists($freeSlots, 'getCollection')) { + throw new \Exception('Invalid response from API'); + } + + return $this->processFreeSlots($freeSlots->getCollection()); + + } catch (\Exception $e) { + error_log('Error in AvailableAppointmentsService: ' . $e->getMessage()); + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An error occurred while fetching available appointments', + 'status' => 500, + ]; + } + } + + private function validateQueryParams($date, $officeId, $serviceIds, $serviceCounts) + { + $errors = []; + + if (!$date) { + $errors[] = [ + 'type' => 'field', + 'msg' => 'date is required and must be a valid date', + 'path' => 'date', + 'location' => 'body' + ]; + } + + if (!$officeId || !is_numeric($officeId)) { + $errors[] = [ + 'type' => 'field', + 'msg' => 'officeId should be a 32-bit integer', + 'path' => 'officeId', + 'location' => 'body' + ]; + } + + if (empty($serviceIds[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceIds))) { + $errors[] = [ + 'type' => 'field', + 'msg' => 'serviceId should be a comma-separated string of integers', + 'path' => 'serviceId', + 'location' => 'body' + ]; + } + + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { + $errors[] = [ + 'type' => 'field', + 'msg' => 'serviceCount should be a comma-separated string of integers', + 'path' => 'serviceCount', + 'location' => 'body' + ]; + } + + return $errors; + } + + + private function processFreeSlots($freeSlots) + { + if (empty($freeSlots)) { + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', + 'status' => 404, + ]; + } + + $currentTimestamp = time(); + $appointmentTimestamps = []; + + foreach ($freeSlots as $slot) { + foreach ($slot->appointments as $appointment) { + $timestamp = (int)$appointment->date; + if (!in_array($timestamp, $appointmentTimestamps) && $timestamp > $currentTimestamp) { + $appointmentTimestamps[] = $timestamp; + } + } + } + + if (empty($appointmentTimestamps)) { + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', + 'status' => 404, + ]; + } + + sort($appointmentTimestamps); + + return [ + 'appointmentTimestamps' => $appointmentTimestamps, + 'lastModified' => round(microtime(true) * 1000), + 'status' => 200, + ]; + } + + private function getInternalDateFromISO($dateString) + { + $date = new \DateTime($dateString); + return [ + 'day' => (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y'), + ]; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php new file mode 100644 index 000000000..fa458eca8 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php @@ -0,0 +1,106 @@ +validateQueryParams($officeId, $serviceId, $startDate, $endDate, $serviceCounts); + + if (!empty($errors)) { + return ['errors' => $errors, 'status' => 400]; + } + + try { + $firstDay = $this->getInternalDateFromISO($startDate); + $lastDay = $this->getInternalDateFromISO($endDate); + + $calendar = new CalendarEntity(); + $calendar->firstDay = $firstDay; + $calendar->lastDay = $lastDay; + $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; + $calendar->requests = [ + [ + 'id' => $serviceId, + 'source' => 'dldb', + 'slotCount' => $serviceCounts, + ] + ]; + + $apiResponse = \App::$http->readPostResult('/calendar/', $calendar); + $calendarEntity = $apiResponse->getEntity(); + $daysCollection = $calendarEntity->days; + $formattedDays = []; + + foreach ($daysCollection as $day) { + $formattedDays[] = sprintf('%04d-%02d-%02d', $day->year, $day->month, $day->day); + } + + if (empty($formattedDays)) { + return [ + 'availableDays' => [], + 'errorCode' => 'noAppointmentForThisScope', + 'errorMessage' => 'No available days found for the given criteria', + 'status' => 404, + ]; + } + + return [ + 'availableDays' => $formattedDays, + 'lastModified' => round(microtime(true) * 1000), + 'status' => 200, + ]; + + } catch (\Exception $e) { + error_log('Error in AvailableDaysService: ' . $e->getMessage()); + return [ + 'availableDays' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine', + 'lastModified' => round(microtime(true) * 1000), + 'status' => 500, + ]; + } + } + + private function validateQueryParams($officeId, $serviceId, $startDate, $endDate, $serviceCounts) + { + $errors = []; + if (!$startDate) { + $errors[] = ['type' => 'field', 'msg' => 'startDate is required and must be a valid date', 'path' => 'startDate', 'location' => 'query']; + } + if (!$endDate) { + $errors[] = ['type' => 'field', 'msg' => 'endDate is required and must be a valid date', 'path' => 'endDate', 'location' => 'query']; + } + if (!$officeId || !is_numeric($officeId)) { + $errors[] = ['type' => 'field', 'msg' => 'officeId should be a 32-bit integer', 'path' => 'officeId', 'location' => 'query']; + } + if (!$serviceId || !is_numeric($serviceId)) { + $errors[] = ['type' => 'field', 'msg' => 'serviceId should be a 32-bit integer', 'path' => 'serviceId', 'location' => 'query']; + } + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { + $errors[] = ['type' => 'field', 'msg' => 'serviceCount should be a comma-separated string of integers', 'path' => 'serviceCount', 'location' => 'query']; + } + + return $errors; + } + + private function getInternalDateFromISO($dateString) + { + $date = new \DateTime($dateString); + return [ + 'day' => (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y'), + ]; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php new file mode 100644 index 000000000..18d38de4b --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php @@ -0,0 +1,18 @@ + Application::$FRIENDLYCAPTCHA_SITEKEY, + 'captchaEndpoint' => Application::$FRIENDLYCAPTCHA_ENDPOINT, + 'puzzle' => Application::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE, + 'captchaEnabled' => Application::$CAPTCHA_ENABLED + ]; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php new file mode 100644 index 000000000..d796547b1 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php @@ -0,0 +1,118 @@ +readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $scopeList = $sources->getScopeList() ?? []; + $providerProjectionList = []; + + foreach ($sources->getProviderList() as $provider) { + $matchingScope = null; + foreach ($scopeList as $scope) { + if ($scope->provider->id == $provider->id) { + $matchingScope = $scope; + break; + } + } + + $providerData = [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ]; + + if ($matchingScope) { + $providerData["scope"] = [ + "id" => $matchingScope->id, + "provider" => $matchingScope->provider, + "shortName" => $matchingScope->shortName, + "telephoneActivated" => $matchingScope->getTelephoneActivated(), + "telephoneRequired" => $matchingScope->getTelephoneRequired(), + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), + "displayInfo" => $matchingScope->getDisplayInfo() + ]; + } + + $providerProjectionList[] = $providerData; + } + + return $providerProjectionList; + } + + public function getOfficesByServiceIds(array $serviceIds) + { + $serviceIds = array_unique($serviceIds); + + if (empty($serviceIds) || $serviceIds == ['']) { + return [ + 'offices' => [], + 'error' => 'Invalid serviceId(s)', + 'status' => 400 + ]; + } + + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $providerList = $sources->getProviderList(); + $requestRelationList = $sources->getRequestRelationList(); + + $offices = []; + $notFoundIds = []; + $addedOfficeIds = []; + + foreach ($serviceIds as $serviceId) { + $found = false; + foreach ($requestRelationList as $relation) { + if ($relation->request->id == $serviceId) { + if (!in_array($relation->provider->id, $addedOfficeIds)) { + foreach ($providerList as $provider) { + if ($provider->id == $relation->provider->id) { + $offices[] = [ + "id" => $provider->id, + "name" => $provider->name, + ]; + $addedOfficeIds[] = $provider->id; + $found = true; + break; + } + } + } else { + $found = true; + } + } + } + if (!$found) { + $notFoundIds[] = $serviceId; + } + } + + if (empty($offices)) { + return [ + 'offices' => [], + 'error' => 'Office(s) not found for the provided serviceId(s)', + 'status' => 404 + ]; + } + + $responseContent = ['offices' => $offices]; + if (!empty($notFoundIds)) { + $responseContent['warning'] = 'The following serviceId(s) were not found: ' . implode(', ', $notFoundIds); + } + + return [ + 'offices' => $responseContent, + 'status' => 200 + ]; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php similarity index 78% rename from zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php rename to zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php index 379e9b196..b2388457f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php @@ -1,22 +1,28 @@ readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - $providerList = $sources->getProviderList() ?? []; $requestList = $sources->getRequestList() ?? []; $relationList = $sources->getRequestRelationList() ?? []; + + $offices = $this->mapOfficesWithScope($sources, $providerList); + $services = $this->mapServicesWithCombinations($requestList, $relationList); + $relations = $this->mapRelations($relationList); + + return [ + 'offices' => $offices, + 'services' => $services, + 'relations' => $relations, + ]; + } + + private function mapOfficesWithScope($sources, $providerList) + { $offices = []; foreach ($providerList as $provider) { $officeData = [ @@ -30,20 +36,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $offices[] = $officeData; } - $services = $this->mapServicesWithCombinations($requestList, $relationList); - $relations = []; - foreach ($relationList as $relation) { - $relations[] = [ - "officeId" => $relation->provider->id, - "serviceId" => $relation->request->id, - "slots" => intval($relation->slots) - ]; - } - return Render::withJson($response, [ - "offices" => $offices, - "services" => $services, - "relations" => $relations, - ]); + return $offices; } private function mapServicesWithCombinations($requestList, $relationList) @@ -55,6 +48,7 @@ private function mapServicesWithCombinations($requestList, $relationList) } $servicesProviderIds[$relation->request->id][] = $relation->provider->id; } + $services = []; foreach ($requestList as $service) { $serviceCombinations = []; @@ -63,26 +57,37 @@ private function mapServicesWithCombinations($requestList, $relationList) "name" => $service->getName(), "maxQuantity" => $service->getAdditionalData()['maxQuantity'] ?? 1, ]; + if (isset($service->getAdditionalData()['combinable'])) { foreach ($service->getAdditionalData()['combinable'] as $combinationServiceId) { $commonProviders = array_intersect( $servicesProviderIds[$service->getId()] ?? [], $servicesProviderIds[$combinationServiceId] ?? [] ); - if (!empty($commonProviders)) { - $serviceCombinations[$combinationServiceId] = array_values($commonProviders); - } else { - $serviceCombinations[$combinationServiceId] = []; - } + $serviceCombinations[$combinationServiceId] = !empty($commonProviders) ? array_values($commonProviders) : []; } $mappedService['combinable'] = $serviceCombinations; - } + } + $services[] = $mappedService; } - + return $services; } - + + private function mapRelations($relationList) + { + $relations = []; + foreach ($relationList as $relation) { + $relations[] = [ + "officeId" => $relation->provider->id, + "serviceId" => $relation->request->id, + "slots" => intval($relation->slots) + ]; + } + return $relations; + } + private function getScopeForProvider($sources, $providerId) { $scopeList = $sources->getScopeList(); @@ -98,6 +103,7 @@ private function getScopeForProvider($sources, $providerId) "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), + "displayInfo" => $scope->getDisplayInfo() ]; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php new file mode 100644 index 000000000..1ba0b7125 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php @@ -0,0 +1,110 @@ +httpClient = $httpClient; + } + + public function getProcessById($processId, $authKey) + { + $resolveReferences = 2; + $process = $this->httpClient->readGetResult("/process/{$processId}/{$authKey}/", [ + 'resolveReferences' => $resolveReferences + ])->getEntity(); + + return $process; + } + + public function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) + { + $requests = []; + + foreach ($serviceIds as $index => $serviceId) { + $count = intval($serviceCounts[$index]); + for ($i = 0; $i < $count; $i++) { + $requests[] = [ + 'id' => $serviceId, + 'source' => 'dldb' + ]; + } + } + + $appointmentProcess['requests'] = $requests; + + return $this->httpClient->readPostResult('/process/status/reserved/', $appointmentProcess)->getEntity(); + } + + public function submitClientData($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return $this->httpClient->readPostResult($url, $process)->getEntity(); + } + + public function preconfirmProcess($process) + { + $url = '/process/status/preconfirmed/'; + return $this->httpClient->readPostResult($url, $process)->getEntity(); + } + + public function confirmProcess($process) + { + $url = '/process/status/confirmed/'; + return $this->httpClient->readPostResult($url, $process)->getEntity(); + } + + public function cancelAppointment($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return $this->httpClient->readDeleteResult($url, $process)->getEntity(); + } + + public function sendConfirmationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; + return $this->httpClient->readPostResult($url, $process)->getEntity(); + } + + public function sendPreconfirmationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; + return $this->httpClient->readPostResult($url, $process)->getEntity(); + } + + public function sendCancelationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; + return $this->httpClient->readPostResult($url, $process)->getEntity(); + } + + public function getFreeDays($providers, $requests, $firstDay, $lastDay) + { + $requestUrl = '/calendar/'; + $dataPayload = [ + 'firstDay' => $firstDay, + 'lastDay' => $lastDay, + 'providers' => $providers, + 'requests' => $requests, + ]; + + return $this->httpClient->readPostResult($requestUrl, $dataPayload)->getEntity(); + } + + public function getFreeTimeslots($providers, $requests, $firstDay, $lastDay) + { + $requestUrl = '/process/status/free/'; + $dataPayload = [ + 'firstDay' => $firstDay, + 'lastDay' => $lastDay, + 'providers' => $providers, + 'requests' => $requests, + ]; + + return $this->httpClient->readPostResult($requestUrl, $dataPayload)->getEntity(); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php similarity index 59% rename from zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php rename to zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php index 790600fc2..7d38326c1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php @@ -1,34 +1,52 @@ readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $scopeList = $sources->getScopeList() ?? []; + $scopesProjectionList = []; + + foreach ($scopeList as $scope) { + $scopesProjectionList[] = [ + "id" => $scope->id, + "provider" => $scope->provider, + "shortName" => $scope->shortName, + "telephoneActivated" => $scope->getTelephoneActivated(), + "telephoneRequired" => $scope->getTelephoneRequired(), + "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() + ]; + } + + return $scopesProjectionList; + } + + public function getScopeByIds(array $scopeIds) { - $scopeIds = explode(',', $request->getQueryParams()['scopeId'] ?? ''); $scopeIds = array_unique($scopeIds); if (empty($scopeIds) || $scopeIds == ['']) { - $responseContent = [ + return [ 'scopes' => [], 'error' => 'Invalid scopeId(s)', + 'status' => 400 ]; - $response = $response->withStatus(400) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($responseContent)); - return $response; } $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); - $scopeList = $sources->getScopeList(); + $scopeList = $sources->getScopeList(); $scopes = []; $notFoundIds = []; @@ -48,26 +66,24 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo "customTextfieldActivated" => $scopeItem->getCustomTextfieldActivated(), "customTextfieldRequired" => $scopeItem->getCustomTextfieldRequired(), "customTextfieldLabel" => $scopeItem->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scopeItem->getCaptchaActivatedRequired(), + "captchaActivatedRequired" => $scopeItem->getCaptchaActivatedRequired() ]; $found = true; break; } } + if (!$found) { $notFoundIds[] = $scopeId; } } if (empty($scopes)) { - $responseContent = [ + return [ 'scopes' => [], 'error' => 'Scope(s) not found', + 'status' => 404 ]; - $response = $response->withStatus(404) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($responseContent)); - return $response; } $responseContent = ['scopes' => $scopes]; @@ -75,9 +91,9 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $responseContent['warning'] = 'The following scopeId(s) were not found: ' . implode(', ', $notFoundIds); } - $response = $response->withStatus(200) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($responseContent)); - return $response; + return [ + 'scopes' => $responseContent, + 'status' => 200 + ]; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php similarity index 63% rename from zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php rename to zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php index 57c988e44..9a90705c8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php @@ -1,32 +1,46 @@ readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $requestList = $sources->getRequestList() ?? []; + $servicesProjectionList = []; + + foreach ($requestList as $request) { + $additionalData = $request->getAdditionalData(); + $servicesProjectionList[] = [ + "id" => $request->getId(), + "name" => $request->getName(), + "maxQuantity" => $additionalData['maxQuantity'] ?? 1, + ]; + } + + return $servicesProjectionList; + } + + public function getServicesByOfficeIds(array $officeIds) { - $officeIds = explode(',', $request->getQueryParams()['officeId'] ?? ''); $officeIds = array_unique($officeIds); if (empty($officeIds) || $officeIds == ['']) { - $responseContent = [ + return [ 'services' => [], 'error' => 'Invalid officeId(s)', + 'status' => 400, ]; - $response = $response->withStatus(400) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($responseContent)); - return $response; } $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); + $requestList = $sources->getRequestList(); $requestRelationList = $sources->getRequestRelationList(); @@ -57,14 +71,11 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo } if (empty($services)) { - $responseContent = [ + return [ 'services' => [], 'error' => 'Service(s) not found for the provided officeId(s)', + 'status' => 404, ]; - $response = $response->withStatus(404) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($responseContent)); - return $response; } $responseContent = ['services' => $services]; @@ -72,9 +83,9 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $responseContent['warning'] = 'The following officeId(s) were not found: ' . implode(', ', $notFoundIds); } - $response = $response->withStatus(200) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($responseContent)); - return $response; + return [ + 'services' => $responseContent, + 'status' => 200, + ]; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php deleted file mode 100644 index 2c15b050d..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php +++ /dev/null @@ -1,33 +0,0 @@ -readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $requestList = $sources->getRequestList() ?? []; - $servicesProjectionList = []; - - foreach ($requestList as $request) { - $additionalData = $request->getAdditionalData(); - $servicesProjectionList[] = [ - "id" => $request->getId(), - "name" => $request->getName(), - "maxQuantity" => $additionalData['maxQuantity'] ?? 1, - ]; - } - - return Render::withJson($response, [ - "services" => $servicesProjectionList, - ]); - } -} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php index a38e2e30b..0f9264589 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php @@ -5,7 +5,7 @@ class AppointmentCancelTest extends Base { - protected $classname = "AppointmentCancel"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentCancel"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php index 04d3d9e25..1a9b3f917 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php @@ -4,7 +4,7 @@ class AppointmentConfirmTest extends Base { - protected $classname = "AppointmentConfirm"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentConfirm"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php index 1b5c757d4..3710bcc3a 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php @@ -6,7 +6,7 @@ class AppointmentGetTest extends Base { - protected $classname = "AppointmentGet"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentGet"; public function testRendering() { diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php index 3208be31f..4d65b967d 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php @@ -5,7 +5,7 @@ class AppointmentPreconfirmTest extends Base { - protected $classname = "AppointmentPreconfirm"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentPreconfirm"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php index 655dbdd0e..ec999ccb0 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -5,7 +5,7 @@ class AppointmentReserveTest extends Base { - protected $classname = "AppointmentReserve"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentReserve"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php index 48279fd9f..2dd462f85 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -5,7 +5,7 @@ class AppointmentUpdateTest extends Base { - protected $classname = "AppointmentUpdate"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentUpdate"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php index 4e9db0c65..d40e1f28b 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php @@ -4,7 +4,7 @@ class AvailableAppointmentsListTest extends Base { - protected $classname = "AvailableAppointmentsList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AvailableAppointmentsList"; public function testRendering() { diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php index 2e5b316d7..bd3113305 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php @@ -4,7 +4,7 @@ class AvailableDaysListTest extends Base { - protected $classname = "AvailableDaysList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\AvailableDaysList"; public function testRendering() { diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index f592448a3..010151f73 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -7,7 +7,7 @@ class CaptchaGetTest extends Base { - protected $classname = "CaptchaGet"; + protected $classname = "\BO\Zmscitizenapi\Controllers\CaptchaGet"; public function setUp(): void { diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php index 8e1b4adbd..182e4b545 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php @@ -5,7 +5,7 @@ class OfficesByServiceListTest extends Base { - protected $classname = "OfficesByServiceList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\OfficesByServiceList"; public function testRendering() { @@ -111,6 +111,7 @@ public function testServiceNotFound() ], []); $expectedResponse = [ + 'status' => 404, 'offices' => [], 'error' => 'Office(s) not found for the provided serviceId(s)' ]; @@ -122,6 +123,7 @@ public function testNoServiceIdProvided() { $response = $this->render([], [], []); $expectedResponse = [ + 'status' => 400, 'offices' => [], 'error' => 'Invalid serviceId(s)' ]; diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php index 959c350dd..06c9ee1de 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php @@ -5,7 +5,7 @@ class OfficesListTest extends Base { - protected $classname = "OfficesList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\OfficesList"; public function testRendering() { $this->setApiCalls([ @@ -37,7 +37,8 @@ public function testRendering() { "customTextfieldActivated" => "1", "customTextfieldRequired" => "0", "customTextfieldLabel" => "Custom Label", - "captchaActivatedRequired" => "1" + "captchaActivatedRequired" => "1", + "displayInfo" => null ] ], [ @@ -56,7 +57,8 @@ public function testRendering() { "customTextfieldActivated" => "0", "customTextfieldRequired" => "1", "customTextfieldLabel" => "", - "captchaActivatedRequired" => "0" + "captchaActivatedRequired" => "0", + "displayInfo" => null ] ] ] diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php index b36009e1a..b312125cf 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php @@ -5,7 +5,7 @@ class OfficesServicesRelationsTest extends Base { - protected $classname = "OfficesServicesRelations"; + protected $classname = "\BO\Zmscitizenapi\Controllers\OfficesServicesRelations"; public function testRendering() { @@ -38,7 +38,8 @@ public function testRendering() "customTextfieldActivated" => "1", "customTextfieldRequired" => "0", "customTextfieldLabel" => "Custom Label", - "captchaActivatedRequired" => "1" + "captchaActivatedRequired" => "1", + "displayInfo" => null ] ], [ @@ -57,7 +58,8 @@ public function testRendering() "customTextfieldActivated" => "0", "customTextfieldRequired" => "1", "customTextfieldLabel" => "", - "captchaActivatedRequired" => "0" + "captchaActivatedRequired" => "0", + "displayInfo" => null ] ] ], diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php index 59eaf0bb5..f3ab70d3a 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -7,7 +7,7 @@ class ScopeByIdGetTest extends Base { - protected $classname = "ScopeByIdGet"; + protected $classname = "\BO\Zmscitizenapi\Controllers\ScopeByIdGet"; public function testRendering() { @@ -113,6 +113,7 @@ public function testScopeNotFound() ], []); $expectedResponse = [ + 'status' => 404, 'scopes' => [], 'error' => 'Scope(s) not found' ]; @@ -124,6 +125,7 @@ public function testNoScopeIdProvided() { $response = $this->render([], [], []); $expectedResponse = [ + 'status' => 400, 'scopes' => [], 'error' => 'Invalid scopeId(s)' ]; diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php index 883b19dd9..de3ee8fa0 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php @@ -5,7 +5,7 @@ class ScopesListTest extends Base { - protected $classname = "ScopesList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\ScopesList"; public function testRendering() { $this->setApiCalls([ diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php index 313692c89..cce6908b5 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php @@ -5,7 +5,7 @@ class ServicesByOfficeListTest extends Base { - protected $classname = "ServicesByOfficeList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\ServicesByOfficeList"; public function testRendering() { @@ -85,6 +85,7 @@ public function testServiceNotFound() ], []); $expectedResponse = [ + 'status' => 404, 'services' => [], 'error' => 'Service(s) not found for the provided officeId(s)' ]; @@ -96,6 +97,7 @@ public function testNoOfficeIdProvided() { $response = $this->render([], [], []); $expectedResponse = [ + 'status' => 400, 'services' => [], 'error' => 'Invalid officeId(s)' ]; diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php index 97ce73cbc..aa72571bb 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php @@ -5,7 +5,7 @@ class ServicesListTest extends Base { - protected $classname = "ServicesList"; + protected $classname = "\BO\Zmscitizenapi\Controllers\ServicesList"; public function testRendering() { $this->setApiCalls([ diff --git a/zmsentities/package-lock.json b/zmsentities/package-lock.json new file mode 100644 index 000000000..19e685ecd --- /dev/null +++ b/zmsentities/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "eappointment-zmsentities", + "version": "file:../../../../zmsentities", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "eappointment-zmsentities", + "version": "1.0.0", + "license": "MIT", + "devDependencies": {} + } + } +} From 686d20a79691a809550b94ec3ff1cb69abf53b78 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 18 Sep 2024 12:45:07 +0200 Subject: [PATCH 06/42] remove duplicate function definitions --- zmscitizenapi/src/Zmscitizenapi/BaseController.php | 8 ++++++++ .../src/Zmscitizenapi/Controllers/AppointmentCancel.php | 1 + .../src/Zmscitizenapi/Controllers/AppointmentGet.php | 9 +-------- .../Controllers/AvailableAppointmentsList.php | 7 ------- .../src/Zmscitizenapi/Controllers/AvailableDaysList.php | 7 ------- .../Zmscitizenapi/Controllers/OfficesByServiceList.php | 7 ------- .../src/Zmscitizenapi/Controllers/ScopeByIdGet.php | 8 -------- .../Zmscitizenapi/Controllers/ServicesByOfficeList.php | 7 ------- 8 files changed, 10 insertions(+), 44 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 55e94fa31..edff468c0 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -23,6 +23,14 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return parent::__invoke($request, $response, $args); } + protected function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface + { + $response = $response->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + $response->getBody()->write(json_encode($content)); + return $response; + } + // ----------------- Helpers --------------------- protected function getInternalDateFromISO($dateString) diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php index fe7292395..482c67655 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php @@ -13,4 +13,5 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo { return Render::withJson($response, []); } + } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentGet.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentGet.php index 5a9d31395..6cb026a9c 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentGet.php @@ -28,12 +28,5 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $result['data'] ?? $result, $result['status']); } - - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); - return $response; - } + } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableAppointmentsList.php index 046aec524..91e0f8c0f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableAppointmentsList.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableAppointmentsList.php @@ -25,11 +25,4 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $result, $result['status']); } - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content, JSON_NUMERIC_CHECK)); - return $response; - } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php index 9719e387d..fda32c4a3 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php @@ -25,11 +25,4 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $result, $result['status']); } - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content, JSON_NUMERIC_CHECK)); - return $response; - } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php index d04c82e77..c3a001396 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php @@ -30,11 +30,4 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $result['offices'], $result['status']); } - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); - return $response; - } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php index 33faf56c0..530d7d86f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php @@ -28,13 +28,5 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $result['scopes'], $result['status']); } - - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); - return $response; - } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php index 654217169..c3401f811 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php @@ -29,11 +29,4 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $result['services'], $result['status']); } - private function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface - { - $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); - return $response; - } } From 983c529d1d5fabf4ef485bd37141b88c4435d685 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 18 Sep 2024 16:22:02 +0200 Subject: [PATCH 07/42] move helpers from base controller to new utility helper --- .../src/Zmscitizenapi/BaseController.php | 79 ------------------- .../Zmscitizenapi/Controllers/CaptchaGet.php | 1 - .../Zmscitizenapi/Helper/UtilityHelper.php | 31 ++++++++ .../Services/AvailableAppointmentsService.php | 17 ++-- .../Services/AvailableDaysService.php | 15 +--- .../Zmscitizenapi/Services/CaptchaService.php | 22 ++++++ .../OfficesServicesRelationsService.php | 53 +++++++++++++ .../Zmscitizenapi/Services/ScopesService.php | 23 ++++++ 8 files changed, 139 insertions(+), 102 deletions(-) create mode 100644 zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index edff468c0..13da97818 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -30,85 +30,6 @@ protected function createJsonResponse(ResponseInterface $response, array $conten $response->getBody()->write(json_encode($content)); return $response; } - - // ----------------- Helpers --------------------- - - protected function getInternalDateFromISO($dateString) - { - $date = new \DateTime($dateString); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y'), - ]; - } - - protected function getInternalDateFromTimestamp(int $timestamp) - { - $date = (new \DateTime())->setTimestamp($timestamp); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y') - ]; - } - - protected function onlyUniqueAppointments($value, $index, $self) - { - return array_search($value, $self) === $index; - } - - protected function getThinnedProcessData($myProcess) - { - if (!$myProcess || !isset($myProcess->id)) { - return []; - } - - $subRequestCounts = []; - $mainServiceId = null; - $mainServiceCount = 0; - - if (isset($myProcess->requests)) { - $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); - if (count($requests) > 0) { - $mainServiceId = $requests[0]->id; - foreach ($requests as $request) { - if ($request->id === $mainServiceId) { - $mainServiceCount++; - } else { - if (!isset($subRequestCounts[$request->id])) { - $subRequestCounts[$request->id] = [ - 'id' => $request->id, - 'count' => 0 - ]; - } - $subRequestCounts[$request->id]['count']++; - } - } - } - } - - return [ - 'processId' => $myProcess->id, - 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, - 'authKey' => $myProcess->authKey ?? null, - 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, - 'customTextfield' => $myProcess->customTextfield ?? null, - 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, - 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, - 'officeName' => $myProcess->scope->contact->name ?? null, - 'officeId' => $myProcess->scope->provider->id ?? null, - 'scope' => $myProcess->scope ?? null, - 'subRequestCounts' => array_values($subRequestCounts), - 'serviceId' => $mainServiceId, - 'serviceCount' => $mainServiceCount - ]; - } - - protected function uniqueElementsFilter($value, $index, $self) - { - return array_search($value, $self) === $index; - } } \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php index e371742f4..4ad734336 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php @@ -14,7 +14,6 @@ class CaptchaGet extends BaseController public function __construct() { - // Initialize the CaptchaService $this->captchaService = new CaptchaService(); } diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php new file mode 100644 index 000000000..37f4f8590 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php @@ -0,0 +1,31 @@ + (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y'), + ]; + } + + public function getInternalDateFromTimestamp(int $timestamp) + { + $date = (new \DateTime())->setTimestamp($timestamp); + return [ + 'day' => (int) $date->format('d'), + 'month' => (int) $date->format('m'), + 'year' => (int) $date->format('Y') + ]; + } + + public function uniqueElementsFilter($value, $index, $self) + { + return array_search($value, $self) === $index; + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php index 569519774..8aa9b9ae6 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php @@ -20,9 +20,11 @@ public function getAvailableAppointments(array $queryParams) } try { + $utilityHelper = new \BO\Zmscitizenapi\Helper\UtilityHelper(); + $calendar = new CalendarEntity(); - $calendar->firstDay = $this->getInternalDateFromISO($date); - $calendar->lastDay = $this->getInternalDateFromISO($date); + $calendar->firstDay = $utilityHelper->getInternalDateFromISO($date); + $calendar->lastDay = $utilityHelper->getInternalDateFromISO($date); $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; $calendar->requests = []; @@ -55,6 +57,8 @@ public function getAvailableAppointments(array $queryParams) } } + + private function validateQueryParams($date, $officeId, $serviceIds, $serviceCounts) { $errors = []; @@ -140,13 +144,4 @@ private function processFreeSlots($freeSlots) ]; } - private function getInternalDateFromISO($dateString) - { - $date = new \DateTime($dateString); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y'), - ]; - } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php index fa458eca8..2cb3b2a28 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php @@ -21,8 +21,10 @@ public function getAvailableDays(array $queryParams) } try { - $firstDay = $this->getInternalDateFromISO($startDate); - $lastDay = $this->getInternalDateFromISO($endDate); + $utilityHelper = new \BO\Zmscitizenapi\Helper\UtilityHelper(); + + $firstDay = $utilityHelper->getInternalDateFromISO($startDate); + $lastDay = $utilityHelper->getInternalDateFromISO($endDate); $calendar = new CalendarEntity(); $calendar->firstDay = $firstDay; @@ -94,13 +96,4 @@ private function validateQueryParams($officeId, $serviceId, $startDate, $endDate return $errors; } - private function getInternalDateFromISO($dateString) - { - $date = new \DateTime($dateString); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y'), - ]; - } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php index 18d38de4b..fc4086d0b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php @@ -2,7 +2,9 @@ namespace BO\Zmscitizenapi\Services; +use GuzzleHttp\Exception\RequestException; use BO\Zmscitizenapi\Application; +use Exception; class CaptchaService { @@ -15,4 +17,24 @@ public function getCaptchaDetails() 'captchaEnabled' => Application::$CAPTCHA_ENABLED ]; } + + public function verifyCaptcha($solution) + { + try { + $response = $this->httpClient->post(Application::$FRIENDLYCAPTCHA_ENDPOINT, [ + 'form_params' => [ + 'secret' => Application::$FRIENDLYCAPTCHA_SECRET, + 'solution' => $solution + ] + ]); + + $responseBody = json_decode($response->getBody(), true); + + return $responseBody; + } catch (RequestException $e) { + $errorMessage = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage(); + error_log('Error verifying captcha: ' . $errorMessage); + throw new Exception('Captcha verification failed'); + } + } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php index b2388457f..aedf672c8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php @@ -109,4 +109,57 @@ private function getScopeForProvider($sources, $providerId) } return null; } + + + public function validateServiceLocationCombination($officeId, array $serviceIds) + { + // Fetch all available services for the given officeId + $availableServices = $this->getServicesProvidedAtOffice($officeId); + $availableServiceIds = array_map(function ($service) { + return $service['id']; + }, $availableServices); + + // Check if there are any invalid service IDs + $invalidServiceIds = array_filter($serviceIds, function ($serviceId) use ($availableServiceIds) { + return !in_array($serviceId, $availableServiceIds); + }); + + if (!empty($invalidServiceIds)) { + return [ + 'status' => 400, + 'errorCode' => 'invalidLocationAndServiceCombination', + 'errorMessage' => 'The provided service(s) do not exist at the given location.', + 'invalidServiceIds' => $invalidServiceIds, + 'locationId' => $officeId, + 'lastModified' => time() * 1000, + ]; + } + + return [ + 'status' => 200, + 'message' => 'Valid service-location combination.', + ]; + } + + public function getServicesProvidedAtOffice($officeId) + { + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $requestList = $sources->getRequestList() ?? []; + $requestRelationList = $sources->getRequestRelationList() ?? []; + + $serviceIds = array_filter($requestRelationList, function ($relation) use ($officeId) { + return $relation->provider->id === $officeId; + }); + + $serviceIds = array_map(function ($relation) { + return $relation->request->id; + }, $serviceIds); + + return array_filter($requestList, function ($request) use ($serviceIds) { + return in_array($request->id, $serviceIds); + }); + } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php index 7d38326c1..7391a841d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php @@ -96,4 +96,27 @@ public function getScopeByIds(array $scopeIds) 'status' => 200 ]; } + + public function getScopeByOfficeId($officeId) + { + try { + $scopes = $this->getScopes(); + + foreach ($scopes as $scope) { + if (isset($scope['provider']) && $scope['provider']->id == $officeId) { + return $scope; + } + } + + return [ + 'error' => 'Scope not found for the provided office ID', + 'status' => 404 + ]; + } catch (\Exception $e) { + return [ + 'error' => 'Error fetching scope by office ID: ' . $e->getMessage(), + 'status' => 500 + ]; + } + } } From 261fd820a0ee09ecd28a921e7ff06095fc04e3d2 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 18 Sep 2024 16:33:52 +0200 Subject: [PATCH 08/42] add a method --- .../Services/AvailableAppointmentsService.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php index 8aa9b9ae6..cd53d7669 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php @@ -57,7 +57,46 @@ public function getAvailableAppointments(array $queryParams) } } + public function getFreeAppointments(array $params) + { + $office = [ + 'id' => $params['officeId'], + 'source' => 'dldb' + ]; + + $requests = []; + + // Loop through service IDs and service counts + foreach ($params['serviceIds'] as $index => $serviceId) { + $service = [ + 'id' => $serviceId, + 'source' => 'dldb', + 'slotCount' => $params['serviceCounts'][$index] + ]; + $requests = array_merge($requests, array_fill(0, $service['slotCount'], $service)); + } + + try { + // Fetch available timeslots + $freeSlots = $this->getFreeTimeslots( + [$office], + $requests, + $params['date'], + $params['date'] + ); + return $this->processFreeSlots($freeSlots); + + } catch (\Exception $e) { + error_log('Error in AvailableAppointmentsService: ' . $e->getMessage()); + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An error occurred while fetching available appointments', + 'status' => 500, + ]; + } + } private function validateQueryParams($date, $officeId, $serviceIds, $serviceCounts) { From 830421fc7e01415f1761bcf686421ce807ae7a86 Mon Sep 17 00:00:00 2001 From: Thomas Fink <53316058+ThomasAFink@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:51:26 +0200 Subject: [PATCH 09/42] Feature mpdzbs 887 zmscitizenapi post reserve appointment and refactor (#574) * endpoint working * fix unit tests * one working test for post-reserve * some clean up * feat(ZMS-2517): fix up response for the reserve post request * feat(ZMS-2517): add more unit tests for validations * feat(ZMS-2517): add more unit tests for validations * feat(ZMS-2517): Zmscitizenapi Refactor Part 1 of 3 move controllers back to root * feat(ZMS-2517): Zmscitizenapi Refactor Part 2 of 3 create ZmsApiFacadeService ZmsApiClientService ValidationService MapperService UtilityHelper and delete fragmented services * feat(ZMS-2517): Zmscitizenapi Refactor Part 3 of 3 clean up validations and unit tests --------- Co-authored-by: Thomas Fink --- zmscitizenapi/bootstrap.php | 2 + zmscitizenapi/routing.php | 32 +- .../{Controllers => }/AppointmentCancel.php | 2 +- .../{Controllers => }/AppointmentConfirm.php | 2 +- .../{Controllers => }/AppointmentGet.php | 15 +- .../AppointmentPreconfirm.php | 2 +- .../src/Zmscitizenapi/AppointmentReserve.php | 110 +++ .../{Controllers => }/AppointmentUpdate.php | 2 +- .../AvailableAppointmentsList.php | 21 + .../{Controllers => }/AvailableDaysList.php | 12 +- .../{Controllers => }/CaptchaGet.php | 14 +- .../Controllers/AppointmentReserve.php | 16 - .../Controllers/AvailableAppointmentsList.php | 28 - .../Zmscitizenapi/Controllers/OfficesList.php | 25 - .../Controllers/OfficesServicesRelations.php | 30 - .../Zmscitizenapi/Controllers/ScopesList.php | 25 - .../Controllers/ServicesList.php | 25 - .../Zmscitizenapi/Helper/UtilityHelper.php | 54 +- .../OfficesByServiceList.php | 15 +- .../src/Zmscitizenapi/OfficesList.php | 18 + .../OfficesServicesRelations.php | 18 + .../{Controllers => }/ScopeByIdGet.php | 17 +- .../src/Zmscitizenapi/ScopesList.php | 18 + .../Services/AppointmentService.php | 122 ---- .../Services/AvailableAppointmentsService.php | 186 ----- .../Services/AvailableDaysService.php | 99 --- .../Zmscitizenapi/Services/CaptchaService.php | 15 +- .../Services/ExceptionService.php | 20 + .../Zmscitizenapi/Services/MapperService.php | 94 +++ .../Zmscitizenapi/Services/OfficesService.php | 118 ---- .../OfficesServicesRelationsService.php | 165 ----- .../Zmscitizenapi/Services/ProcessService.php | 110 --- .../Zmscitizenapi/Services/ScopesService.php | 122 ---- .../Services/ServicesService.php | 91 --- .../Services/ValidationService.php | 313 +++++++++ .../Services/ZmsApiClientService.php | 186 +++++ .../Services/ZmsApiFacadeService.php | 641 ++++++++++++++++++ .../ServicesByOfficeList.php | 14 +- .../src/Zmscitizenapi/ServicesList.php | 18 + .../Zmscitizenapi/AppointmentCancelTest.php | 2 +- .../Zmscitizenapi/AppointmentConfirmTest.php | 2 +- .../Zmscitizenapi/AppointmentGetTest.php | 272 +++++--- .../AppointmentPreconfirmTest.php | 2 +- .../Zmscitizenapi/AppointmentReserveTest.php | 445 +++++++++++- .../Zmscitizenapi/AppointmentUpdateTest.php | 2 +- .../AvailableAppointmentsListTest.php | 269 ++++---- .../Zmscitizenapi/AvailableDaysListTest.php | 476 +++++++++---- .../tests/Zmscitizenapi/CaptchaGetTest.php | 10 +- .../OfficesByServiceListTest.php | 45 +- .../tests/Zmscitizenapi/OfficesListTest.php | 21 +- .../OfficesServicesRelationsTest.php | 15 +- .../tests/Zmscitizenapi/ScopeByIdGetTest.php | 52 +- .../tests/Zmscitizenapi/ScopesListTest.php | 23 +- .../ServicesByOfficeListTest.php | 44 +- .../tests/Zmscitizenapi/ServicesListTest.php | 15 +- .../fixtures/GET_SourceGet_dldb.json | 6 +- .../fixtures/GET_appointments.json | 6 +- .../fixtures/GET_appointments_free.json | 111 +++ .../fixtures/GET_reserve_SourceGet_dldb.json | 143 ++++ .../fixtures/POST_reserve_timeslot.json | 100 +++ 60 files changed, 3198 insertions(+), 1680 deletions(-) rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/AppointmentCancel.php (90%) rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/AppointmentConfirm.php (89%) rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/AppointmentGet.php (55%) rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/AppointmentPreconfirm.php (90%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/AppointmentUpdate.php (89%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/AvailableDaysList.php (57%) rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/CaptchaGet.php (50%) delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentReserve.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableAppointmentsList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/OfficesByServiceList.php (61%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/OfficesList.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/ScopeByIdGet.php (62%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/ScopesList.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php delete mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php rename zmscitizenapi/src/Zmscitizenapi/{Controllers => }/ServicesByOfficeList.php (62%) create mode 100644 zmscitizenapi/src/Zmscitizenapi/ServicesList.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json diff --git a/zmscitizenapi/bootstrap.php b/zmscitizenapi/bootstrap.php index e088a568a..708e14373 100644 --- a/zmscitizenapi/bootstrap.php +++ b/zmscitizenapi/bootstrap.php @@ -22,6 +22,8 @@ // Set option for environment, routing, logging and templating \BO\Slim\Bootstrap::init(); +\App::$slim->addBodyParsingMiddleware(); + \App::$http = new \BO\Zmsclient\Http(\App::ZMS_API_URL); //\BO\Zmsclient\Psr7\Client::$curlopt = \App::$http_curl_config; diff --git a/zmscitizenapi/routing.php b/zmscitizenapi/routing.php index d95628c82..5060cb8e6 100644 --- a/zmscitizenapi/routing.php +++ b/zmscitizenapi/routing.php @@ -24,7 +24,7 @@ */ \App::$slim->get( '/services/', - '\BO\Zmscitizenapi\Controllers\ServicesList' + '\BO\Zmscitizenapi\ServicesList' ) ->setName("ServicesList"); @@ -48,7 +48,7 @@ */ \App::$slim->get( '/scopes/', - '\BO\Zmscitizenapi\Controllers\ScopesList' + '\BO\Zmscitizenapi\ScopesList' ) ->setName("ScopesList"); @@ -72,7 +72,7 @@ */ \App::$slim->get( '/offices/', - '\BO\Zmscitizenapi\Controllers\OfficesList' + '\BO\Zmscitizenapi\OfficesList' ) ->setName("OfficesList"); @@ -96,7 +96,7 @@ */ \App::$slim->get( '/offices-and-services/', - '\BO\Zmscitizenapi\Controllers\OfficesServicesRelations' + '\BO\Zmscitizenapi\OfficesServicesRelations' ) ->setName("OfficesServicesRelations"); @@ -128,7 +128,7 @@ */ \App::$slim->get( '/scope-by-id/', - '\BO\Zmscitizenapi\Controllers\ScopeByIdGet' + '\BO\Zmscitizenapi\ScopeByIdGet' ) ->setName("ScopeByIdGet"); @@ -158,7 +158,7 @@ */ \App::$slim->get( '/services-by-office/', - '\BO\Zmscitizenapi\Controllers\ServicesByOfficeList' + '\BO\Zmscitizenapi\ServicesByOfficeList' ) ->setName("ServicesByOfficeList"); @@ -188,7 +188,7 @@ */ \App::$slim->get( '/offices-by-service/', - '\BO\Zmscitizenapi\Controllers\OfficesByServiceList' + '\BO\Zmscitizenapi\OfficesByServiceList' ) ->setName("OfficesByServiceList"); @@ -223,7 +223,7 @@ */ \App::$slim->get( '/available-days/', - '\BO\Zmscitizenapi\Controllers\AvailableDaysList' + '\BO\Zmscitizenapi\AvailableDaysList' ) ->setName("AvailableDaysList"); @@ -263,7 +263,7 @@ */ \App::$slim->get( '/available-appointments/', - '\BO\Zmscitizenapi\Controllers\AvailableAppointmentsList' + '\BO\Zmscitizenapi\AvailableAppointmentsList' ) ->setName("AvailableAppointmentsList"); @@ -318,7 +318,7 @@ */ \App::$slim->get( '/appointment/', - '\BO\Zmscitizenapi\Controllers\AppointmentGet' + '\BO\Zmscitizenapi\AppointmentGet' ) ->setName("AppointmentGet"); @@ -342,7 +342,7 @@ */ \App::$slim->get( '/captcha-details/', - '\BO\Zmscitizenapi\Controllers\CaptchaGet' + '\BO\Zmscitizenapi\CaptchaGet' ) ->setName("CaptchaGet"); @@ -393,7 +393,7 @@ */ \App::$slim->post( '/reserve-appointment/', - '\BO\Zmscitizenapi\Controllers\AppointmentReserve' + '\BO\Zmscitizenapi\AppointmentReserve' ) ->setName("AppointmentReserve"); @@ -444,7 +444,7 @@ */ \App::$slim->post( '/update-appointment/', - '\BO\Zmscitizenapi\Controllers\AppointmentUpdate' + '\BO\Zmscitizenapi\AppointmentUpdate' ) ->setName("AppointmentUpdate"); @@ -495,7 +495,7 @@ */ \App::$slim->post( '/confirm-appointment/', - '\BO\Zmscitizenapi\Controllers\AppointmentConfirm' + '\BO\Zmscitizenapi\AppointmentConfirm' ) ->setName("AppointmentConfirm"); @@ -546,7 +546,7 @@ */ \App::$slim->post( '/preconfirm-appointment/', - '\BO\Zmscitizenapi\Controllers\AppointmentPreconfirm' + '\BO\Zmscitizenapi\AppointmentPreconfirm' ) ->setName("AppointmentPreconfirm"); @@ -597,6 +597,6 @@ */ \App::$slim->post( '/cancel-appointment/', - '\BO\Zmscitizenapi\Controllers\AppointmentCancel' + '\BO\Zmscitizenapi\AppointmentCancel' ) ->setName("AppointmentCancel"); diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php similarity index 90% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php rename to zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php index 482c67655..bd7e6c06f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentCancel.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php @@ -1,6 +1,6 @@ appointmentService = new AppointmentService($processService); - } - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $queryParams = $request->getQueryParams(); $processId = $queryParams['processId'] ?? null; $authKey = $queryParams['authKey'] ?? null; - $result = $this->appointmentService->getAppointmentById($processId, $authKey); + $result = ZmsApiFacadeService::getProcessById($processId, $authKey); return $this->createJsonResponse($response, $result['data'] ?? $result, $result['status']); } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentPreconfirm.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php similarity index 90% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentPreconfirm.php rename to zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php index 03868f26c..f1bb45fa8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentPreconfirm.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php @@ -1,6 +1,6 @@ getParsedBody(); + + $officeId = $body['officeId'] ?? null; + $serviceIds = $body['serviceId'] ?? []; + $serviceCounts = $body['serviceCount'] ?? [1]; + $captchaSolution = $body['captchaSolution'] ?? null; + $timestamp = $body['timestamp'] ?? null; + + + $errors = ValidationService::validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $captchaSolution, $timestamp); + if (!empty($errors['errors'])) { + return $this->createJsonResponse($response, + $errors, 400); + } + + try { + $providerScope = ZmsApiFacadeService::getScopeByOfficeId($officeId); + $captchaRequired = Application::$CAPTCHA_ENABLED === "1" && $providerScope['captchaActivatedRequired'] === "1"; + + if ($captchaRequired) { + $captchaVerificationResult = CaptchaService::verifyCaptcha($captchaSolution); + if (!$captchaVerificationResult['success']) { + return $this->createJsonResponse($response, [ + 'errorCode' => 'captchaVerificationFailed', + 'errorMessage' => 'Captcha verification failed' + ], 400); + } + } + + $serviceValidationResult = ValidationService::validateServiceLocationCombination($officeId, $serviceIds); + if ($serviceValidationResult['status'] !== 200) { + return $this->createJsonResponse($response, $serviceValidationResult, 400); + } + + $freeAppointments = ZmsApiFacadeService::getFreeAppointments([ + 'officeId' => $officeId, + 'serviceIds' => $serviceIds, + 'serviceCounts' => $serviceCounts, + 'date' => UtilityHelper::getInternalDateFromTimestamp($timestamp) + ]); + + $selectedProcess = array_filter($freeAppointments, function ($process) use ($timestamp) { + if (!isset($process['appointments']) || !is_array($process['appointments'])) { + return false; + } + return in_array($timestamp, array_column($process['appointments'], 'date')); + }); + + $errors = ValidationService::validateGetProcessNotFound($selectedProcess); + if (!empty($errors['errors'])) { + return $this->createJsonResponse($response, + $errors, 404); + } + + $selectedProcess = array_values($selectedProcess)[0]; + $selectedProcess['clients'] = [ + [ + 'email' => 'test@muenchen.de' + ] + ]; + + $reservedProcess = ZmsApiFacadeService::reserveTimeslot($selectedProcess, $serviceIds, $serviceCounts); + + if ($reservedProcess && $reservedProcess->scope && $reservedProcess->scope->id) { + $scopeIds = [$reservedProcess->scope->id]; + $scopesData = ZmsApiFacadeService::getScopeByIds($scopeIds); + + if ($scopesData['status'] === 200 && isset($scopesData['scopes']['scopes']) && !empty($scopesData['scopes']['scopes'])) { + $reservedProcess->scope = MapperService::mapScope($scopesData['scopes']['scopes'][0]); + } + } + + $thinnedProcessData = UtilityHelper::getThinnedProcessData($reservedProcess); + $thinnedProcessData = array_merge($thinnedProcessData, ['officeId' => $officeId]); + + return $this->createJsonResponse($response, $thinnedProcessData, 200); + + } catch (\Exception $e) { + return [ + 'errorCode' => 'unexpectedError', + 'errorMessage' => 'Unexpected error: ' . $e->getMessage(), + 'status' => 500, + ]; + } + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php similarity index 89% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentUpdate.php rename to zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index 52d410c13..09a6fe3b0 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -1,6 +1,6 @@ getQueryParams(); + + $result = ZmsApiFacadeService::getAvailableAppointments($queryParams); + + return $this->createJsonResponse($response, $result, statusCode: $result['status']); + } + +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php similarity index 57% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php rename to zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php index fda32c4a3..b614eb978 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AvailableDaysList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php @@ -1,26 +1,20 @@ availableDaysService = new AvailableDaysService(); - } public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $queryParams = $request->getQueryParams(); - $result = $this->availableDaysService->getAvailableDays($queryParams); + $result = ZmsApiFacadeService::getBookableFreeDays($queryParams); return $this->createJsonResponse($response, $result, $result['status']); } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php similarity index 50% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php rename to zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index 4ad734336..4dcbed6b9 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -1,26 +1,18 @@ captchaService = new CaptchaService(); - } - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - $captchaDetails = $this->captchaService->getCaptchaDetails(); + $captchaDetails = CaptchaService::getCaptchaDetails(); - return Render::withJson($response, $captchaDetails); + return $this->createJsonResponse($response, $captchaDetails, statusCode: $captchaDetails['status']); } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentReserve.php deleted file mode 100644 index 6c91f09f1..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/AppointmentReserve.php +++ /dev/null @@ -1,16 +0,0 @@ -availableAppointmentsService = new AvailableAppointmentsService(); - } - - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) - { - $queryParams = $request->getQueryParams(); - - $result = $this->availableAppointmentsService->getAvailableAppointments($queryParams); - - return $this->createJsonResponse($response, $result, $result['status']); - } - -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php deleted file mode 100644 index c19bf596c..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesList.php +++ /dev/null @@ -1,25 +0,0 @@ -officesService = new OfficesService(); // No container - } - - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) - { - $offices = $this->officesService->getOffices(); - return Render::withJson($response, ["offices" => $offices]); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php deleted file mode 100644 index 98c05becb..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesServicesRelations.php +++ /dev/null @@ -1,30 +0,0 @@ -officesServicesRelationsService = new OfficesServicesRelationsService(); // No container - } - - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) - { - $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $data = $this->officesServicesRelationsService->getOfficesServicesRelations($sources); - - return Render::withJson($response, $data); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php deleted file mode 100644 index b522923e8..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopesList.php +++ /dev/null @@ -1,25 +0,0 @@ -scopesService = new ScopesService(); - } - - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) - { - $scopes = $this->scopesService->getScopes(); - return Render::withJson($response, ["scopes" => $scopes]); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php deleted file mode 100644 index dd34da105..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesList.php +++ /dev/null @@ -1,25 +0,0 @@ -servicesService = new ServicesService(); - } - - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) - { - $services = $this->servicesService->getServices(); - return Render::withJson($response, ["services" => $services]); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php index 37f4f8590..e3b772ee1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php @@ -4,7 +4,7 @@ class UtilityHelper { - public function getInternalDateFromISO($dateString) + public static function getInternalDateFromISO($dateString) { $date = new \DateTime($dateString); return [ @@ -14,7 +14,7 @@ public function getInternalDateFromISO($dateString) ]; } - public function getInternalDateFromTimestamp(int $timestamp) + public static function getInternalDateFromTimestamp(int $timestamp) { $date = (new \DateTime())->setTimestamp($timestamp); return [ @@ -24,8 +24,56 @@ public function getInternalDateFromTimestamp(int $timestamp) ]; } - public function uniqueElementsFilter($value, $index, $self) + public static function uniqueElementsFilter($value, $index, $self) { return array_search($value, $self) === $index; } + + public static function getThinnedProcessData($myProcess) + { + if (!$myProcess || !isset($myProcess->id)) { + return []; + } + + $subRequestCounts = []; + $mainServiceId = null; + $mainServiceCount = 0; + + if (isset($myProcess->requests)) { + $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); + if (count($requests) > 0) { + $mainServiceId = $requests[0]->id; + foreach ($requests as $request) { + if ($request->id === $mainServiceId) { + $mainServiceCount++; + } else { + if (!isset($subRequestCounts[$request->id])) { + $subRequestCounts[$request->id] = [ + 'id' => $request->id, + 'count' => 0, + ]; + } + $subRequestCounts[$request->id]['count']++; + } + } + } + } + + return [ + 'processId' => $myProcess->id, + 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, + 'authKey' => $myProcess->authKey ?? null, + 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, + 'customTextfield' => $myProcess->customTextfield ?? null, + 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, + 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, + 'officeName' => $myProcess->scope->contact->name ?? null, + 'officeId' => $myProcess->scope->provider->id ?? null, + 'scope' => $myProcess->scope ?? null, + 'subRequestCounts' => array_values($subRequestCounts), + 'serviceId' => $mainServiceId, + 'serviceCount' => $mainServiceCount, + ]; + } + } diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php similarity index 61% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php rename to zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php index c3a001396..dc3c2e475 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -1,29 +1,22 @@ officesService = new OfficesService(); - } public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $serviceIds = explode(',', $request->getQueryParams()['serviceId'] ?? ''); - $result = $this->officesService->getOfficesByServiceIds($serviceIds); + $result = ZmsApiFacadeService::getOfficesByServiceIds($serviceIds); - if (isset($result['error'])) { + if (isset($result['errors'])) { return $this->createJsonResponse($response, $result, $result['status']); } diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php new file mode 100644 index 000000000..27bb1ed5b --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $offices, statusCode: $offices['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php new file mode 100644 index 000000000..9e89ad4bb --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $officesAndServices, statusCode: $officesAndServices['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php similarity index 62% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php rename to zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php index 530d7d86f..ba57dd411 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -1,30 +1,23 @@ scopesService = new ScopesService(); - } - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $scopeIds = explode(',', $request->getQueryParams()['scopeId'] ?? ''); - $result = $this->scopesService->getScopeByIds($scopeIds); + $result = ZmsApiFacadeService::getScopeByIds($scopeIds); - if (isset($result['error'])) { + if (isset($result['errors'])) { return $this->createJsonResponse($response, $result, $result['status']); - } + } return $this->createJsonResponse($response, $result['scopes'], $result['status']); } diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php new file mode 100644 index 000000000..e8a8d1319 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $scopes, statusCode: $scopes['status']); + } +} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php deleted file mode 100644 index 1cb59ea43..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/AppointmentService.php +++ /dev/null @@ -1,122 +0,0 @@ -processService = $processService; - } - - public function getAppointmentById($processId, $authKey) - { - $errors = $this->validateInputs($processId, $authKey); - if (!empty($errors)) { - return ['errors' => $errors, 'status' => 400]; - } - - try { - $process = $this->processService->getProcessById($processId, $authKey); - - if (!$process) { - return [ - 'errorMessage' => 'Termin wurde nicht gefunden', - 'status' => 404, - ]; - } - - $responseData = $this->getThinnedProcessData($process); - return ['data' => $responseData, 'status' => 200]; - - } catch (\Exception $e) { - if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { - return [ - 'errorMessage' => 'Termin wurde nicht gefunden', - 'status' => 404, - ]; - } else { - return [ - 'error' => 'Unexpected error: ' . $e->getMessage(), - 'status' => 500, - ]; - } - } - } - - private function validateInputs($processId, $authKey) - { - $errors = []; - - if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'processId should be a 32-bit integer', - 'path' => 'processId', - 'location' => 'query' - ]; - } - - if (!$authKey || !is_string($authKey)) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'authKey should be a string', - 'path' => 'authKey', - 'location' => 'query' - ]; - } - - return $errors; - } - - private function getThinnedProcessData($myProcess) - { - if (!$myProcess || !isset($myProcess->id)) { - return []; - } - - $subRequestCounts = []; - $mainServiceId = null; - $mainServiceCount = 0; - - if (isset($myProcess->requests)) { - $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); - if (count($requests) > 0) { - $mainServiceId = $requests[0]->id; - foreach ($requests as $request) { - if ($request->id === $mainServiceId) { - $mainServiceCount++; - } else { - if (!isset($subRequestCounts[$request->id])) { - $subRequestCounts[$request->id] = [ - 'id' => $request->id, - 'count' => 0, - ]; - } - $subRequestCounts[$request->id]['count']++; - } - } - } - } - - return [ - 'processId' => $myProcess->id, - 'timestamp' => isset($myProcess->appointments[0]) ? $myProcess->appointments[0]->date : null, - 'authKey' => $myProcess->authKey ?? null, - 'familyName' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->familyName : null, - 'customTextfield' => $myProcess->customTextfield ?? null, - 'email' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->email : null, - 'telephone' => isset($myProcess->clients[0]) ? $myProcess->clients[0]->telephone : null, - 'officeName' => $myProcess->scope->contact->name ?? null, - 'officeId' => $myProcess->scope->provider->id ?? null, - 'scope' => $myProcess->scope ?? null, - 'subRequestCounts' => array_values($subRequestCounts), - 'serviceId' => $mainServiceId, - 'serviceCount' => $mainServiceCount, - ]; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php deleted file mode 100644 index cd53d7669..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableAppointmentsService.php +++ /dev/null @@ -1,186 +0,0 @@ -validateQueryParams($date, $officeId, $serviceIds, $serviceCounts); - - if (!empty($errors)) { - return ['errors' => $errors, 'status' => 400]; - } - - try { - $utilityHelper = new \BO\Zmscitizenapi\Helper\UtilityHelper(); - - $calendar = new CalendarEntity(); - $calendar->firstDay = $utilityHelper->getInternalDateFromISO($date); - $calendar->lastDay = $utilityHelper->getInternalDateFromISO($date); - $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; - - $calendar->requests = []; - foreach ($serviceIds as $index => $serviceId) { - $slotCount = isset($serviceCounts[$index]) ? intval($serviceCounts[$index]) : 1; - for ($i = 0; $i < $slotCount; $i++) { - $calendar->requests[] = [ - 'id' => $serviceId, - 'source' => 'dldb', - 'slotCount' => 1, - ]; - } - } - - $freeSlots = \App::$http->readPostResult('/process/status/free/', $calendar); - if (!$freeSlots || !method_exists($freeSlots, 'getCollection')) { - throw new \Exception('Invalid response from API'); - } - - return $this->processFreeSlots($freeSlots->getCollection()); - - } catch (\Exception $e) { - error_log('Error in AvailableAppointmentsService: ' . $e->getMessage()); - return [ - 'appointmentTimestamps' => [], - 'errorCode' => 'internalError', - 'errorMessage' => 'An error occurred while fetching available appointments', - 'status' => 500, - ]; - } - } - - public function getFreeAppointments(array $params) - { - $office = [ - 'id' => $params['officeId'], - 'source' => 'dldb' - ]; - - $requests = []; - - // Loop through service IDs and service counts - foreach ($params['serviceIds'] as $index => $serviceId) { - $service = [ - 'id' => $serviceId, - 'source' => 'dldb', - 'slotCount' => $params['serviceCounts'][$index] - ]; - $requests = array_merge($requests, array_fill(0, $service['slotCount'], $service)); - } - - try { - // Fetch available timeslots - $freeSlots = $this->getFreeTimeslots( - [$office], - $requests, - $params['date'], - $params['date'] - ); - - return $this->processFreeSlots($freeSlots); - - } catch (\Exception $e) { - error_log('Error in AvailableAppointmentsService: ' . $e->getMessage()); - return [ - 'appointmentTimestamps' => [], - 'errorCode' => 'internalError', - 'errorMessage' => 'An error occurred while fetching available appointments', - 'status' => 500, - ]; - } - } - - private function validateQueryParams($date, $officeId, $serviceIds, $serviceCounts) - { - $errors = []; - - if (!$date) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'date is required and must be a valid date', - 'path' => 'date', - 'location' => 'body' - ]; - } - - if (!$officeId || !is_numeric($officeId)) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'officeId should be a 32-bit integer', - 'path' => 'officeId', - 'location' => 'body' - ]; - } - - if (empty($serviceIds[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceIds))) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'serviceId should be a comma-separated string of integers', - 'path' => 'serviceId', - 'location' => 'body' - ]; - } - - if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { - $errors[] = [ - 'type' => 'field', - 'msg' => 'serviceCount should be a comma-separated string of integers', - 'path' => 'serviceCount', - 'location' => 'body' - ]; - } - - return $errors; - } - - - private function processFreeSlots($freeSlots) - { - if (empty($freeSlots)) { - return [ - 'appointmentTimestamps' => [], - 'errorCode' => 'appointmentNotAvailable', - 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', - 'status' => 404, - ]; - } - - $currentTimestamp = time(); - $appointmentTimestamps = []; - - foreach ($freeSlots as $slot) { - foreach ($slot->appointments as $appointment) { - $timestamp = (int)$appointment->date; - if (!in_array($timestamp, $appointmentTimestamps) && $timestamp > $currentTimestamp) { - $appointmentTimestamps[] = $timestamp; - } - } - } - - if (empty($appointmentTimestamps)) { - return [ - 'appointmentTimestamps' => [], - 'errorCode' => 'appointmentNotAvailable', - 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', - 'status' => 404, - ]; - } - - sort($appointmentTimestamps); - - return [ - 'appointmentTimestamps' => $appointmentTimestamps, - 'lastModified' => round(microtime(true) * 1000), - 'status' => 200, - ]; - } - -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php b/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php deleted file mode 100644 index 2cb3b2a28..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/AvailableDaysService.php +++ /dev/null @@ -1,99 +0,0 @@ -validateQueryParams($officeId, $serviceId, $startDate, $endDate, $serviceCounts); - - if (!empty($errors)) { - return ['errors' => $errors, 'status' => 400]; - } - - try { - $utilityHelper = new \BO\Zmscitizenapi\Helper\UtilityHelper(); - - $firstDay = $utilityHelper->getInternalDateFromISO($startDate); - $lastDay = $utilityHelper->getInternalDateFromISO($endDate); - - $calendar = new CalendarEntity(); - $calendar->firstDay = $firstDay; - $calendar->lastDay = $lastDay; - $calendar->providers = [['id' => $officeId, 'source' => 'dldb']]; - $calendar->requests = [ - [ - 'id' => $serviceId, - 'source' => 'dldb', - 'slotCount' => $serviceCounts, - ] - ]; - - $apiResponse = \App::$http->readPostResult('/calendar/', $calendar); - $calendarEntity = $apiResponse->getEntity(); - $daysCollection = $calendarEntity->days; - $formattedDays = []; - - foreach ($daysCollection as $day) { - $formattedDays[] = sprintf('%04d-%02d-%02d', $day->year, $day->month, $day->day); - } - - if (empty($formattedDays)) { - return [ - 'availableDays' => [], - 'errorCode' => 'noAppointmentForThisScope', - 'errorMessage' => 'No available days found for the given criteria', - 'status' => 404, - ]; - } - - return [ - 'availableDays' => $formattedDays, - 'lastModified' => round(microtime(true) * 1000), - 'status' => 200, - ]; - - } catch (\Exception $e) { - error_log('Error in AvailableDaysService: ' . $e->getMessage()); - return [ - 'availableDays' => [], - 'errorCode' => 'internalError', - 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine', - 'lastModified' => round(microtime(true) * 1000), - 'status' => 500, - ]; - } - } - - private function validateQueryParams($officeId, $serviceId, $startDate, $endDate, $serviceCounts) - { - $errors = []; - if (!$startDate) { - $errors[] = ['type' => 'field', 'msg' => 'startDate is required and must be a valid date', 'path' => 'startDate', 'location' => 'query']; - } - if (!$endDate) { - $errors[] = ['type' => 'field', 'msg' => 'endDate is required and must be a valid date', 'path' => 'endDate', 'location' => 'query']; - } - if (!$officeId || !is_numeric($officeId)) { - $errors[] = ['type' => 'field', 'msg' => 'officeId should be a 32-bit integer', 'path' => 'officeId', 'location' => 'query']; - } - if (!$serviceId || !is_numeric($serviceId)) { - $errors[] = ['type' => 'field', 'msg' => 'serviceId should be a 32-bit integer', 'path' => 'serviceId', 'location' => 'query']; - } - if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { - $errors[] = ['type' => 'field', 'msg' => 'serviceCount should be a comma-separated string of integers', 'path' => 'serviceCount', 'location' => 'query']; - } - - return $errors; - } - -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php index fc4086d0b..6af881555 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php @@ -8,20 +8,22 @@ class CaptchaService { - public function getCaptchaDetails() + + public static function getCaptchaDetails() { return [ 'siteKey' => Application::$FRIENDLYCAPTCHA_SITEKEY, 'captchaEndpoint' => Application::$FRIENDLYCAPTCHA_ENDPOINT, 'puzzle' => Application::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE, - 'captchaEnabled' => Application::$CAPTCHA_ENABLED + 'captchaEnabled' => Application::$CAPTCHA_ENABLED, + 'status' => 200 ]; } - public function verifyCaptcha($solution) + public static function verifyCaptcha($solution) { try { - $response = $this->httpClient->post(Application::$FRIENDLYCAPTCHA_ENDPOINT, [ + $response = \App::$http->post(Application::$FRIENDLYCAPTCHA_ENDPOINT, [ 'form_params' => [ 'secret' => Application::$FRIENDLYCAPTCHA_SECRET, 'solution' => $solution @@ -33,8 +35,9 @@ public function verifyCaptcha($solution) return $responseBody; } catch (RequestException $e) { $errorMessage = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage(); - error_log('Error verifying captcha: ' . $errorMessage); - throw new Exception('Captcha verification failed'); + //error_log('Error verifying captcha: ' . $errorMessage); + throw new Exception('Captcha verification failed.'); } } } + diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php new file mode 100644 index 000000000..dec1e7a9a --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php @@ -0,0 +1,20 @@ + 'noAppointmentForThisOffice', + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', + 'status' => 404, + ]; + + return ['errors' => $errors, 'status' => 404]; + + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php new file mode 100644 index 000000000..e0738beed --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -0,0 +1,94 @@ + $scope['id'] ?? null, + 'provider' => [ + 'id' => $scope['provider']['id'] ?? null, + 'source' => $scope['provider']['source'] ?? null, + ], + 'shortName' => $scope['shortName'] ?? null, + 'telephoneActivated' => $scope['telephoneActivated'] ?? null, + 'telephoneRequired' => $scope['telephoneRequired'] ?? null, + 'customTextfieldActivated' => $scope['customTextfieldActivated'] ?? null, + 'customTextfieldRequired' => $scope['customTextfieldRequired'] ?? null, + 'customTextfieldLabel' => $scope['customTextfieldLabel'] ?? null, + 'captchaActivatedRequired' => $scope['captchaActivatedRequired'] ?? null, + 'displayInfo' => $scope['displayInfo'] ?? null, + ]; + } + + public static function mapOfficesWithScope($providerList) + { + $offices = []; + foreach ($providerList as $provider) { + $officeData = [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ]; + $scope = ZmsApiFacadeService::getScopeForProvider($provider->id); + if ($scope) { + $officeData['scope'] = $scope; + } + + $offices[] = $officeData; + } + return $offices; + } + + public static function mapServicesWithCombinations($requestList, $relationList) + { + $servicesProviderIds = []; + foreach ($relationList as $relation) { + if (!isset($servicesProviderIds[$relation->request->id])) { + $servicesProviderIds[$relation->request->id] = []; + } + $servicesProviderIds[$relation->request->id][] = $relation->provider->id; + } + + $services = []; + foreach ($requestList as $service) { + $serviceCombinations = []; + $mappedService = [ + "id" => $service->getId(), + "name" => $service->getName(), + "maxQuantity" => $service->getAdditionalData()['maxQuantity'] ?? 1, + ]; + + if (isset($service->getAdditionalData()['combinable'])) { + foreach ($service->getAdditionalData()['combinable'] as $combinationServiceId) { + $commonProviders = array_intersect( + $servicesProviderIds[$service->getId()] ?? [], + $servicesProviderIds[$combinationServiceId] ?? [] + ); + $serviceCombinations[$combinationServiceId] = !empty($commonProviders) ? array_values($commonProviders) : []; + } + $mappedService['combinable'] = $serviceCombinations; + } + + $services[] = $mappedService; + } + + return $services; + } + + public static function mapRelations($relationList) + { + $relations = []; + foreach ($relationList as $relation) { + $relations[] = [ + "officeId" => $relation->provider->id, + "serviceId" => $relation->request->id, + "slots" => intval($relation->slots) + ]; + } + return $relations; + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php deleted file mode 100644 index d796547b1..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesService.php +++ /dev/null @@ -1,118 +0,0 @@ -readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $scopeList = $sources->getScopeList() ?? []; - $providerProjectionList = []; - - foreach ($sources->getProviderList() as $provider) { - $matchingScope = null; - foreach ($scopeList as $scope) { - if ($scope->provider->id == $provider->id) { - $matchingScope = $scope; - break; - } - } - - $providerData = [ - "id" => $provider->id, - "name" => $provider->displayName ?? $provider->name, - ]; - - if ($matchingScope) { - $providerData["scope"] = [ - "id" => $matchingScope->id, - "provider" => $matchingScope->provider, - "shortName" => $matchingScope->shortName, - "telephoneActivated" => $matchingScope->getTelephoneActivated(), - "telephoneRequired" => $matchingScope->getTelephoneRequired(), - "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), - "displayInfo" => $matchingScope->getDisplayInfo() - ]; - } - - $providerProjectionList[] = $providerData; - } - - return $providerProjectionList; - } - - public function getOfficesByServiceIds(array $serviceIds) - { - $serviceIds = array_unique($serviceIds); - - if (empty($serviceIds) || $serviceIds == ['']) { - return [ - 'offices' => [], - 'error' => 'Invalid serviceId(s)', - 'status' => 400 - ]; - } - - $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $providerList = $sources->getProviderList(); - $requestRelationList = $sources->getRequestRelationList(); - - $offices = []; - $notFoundIds = []; - $addedOfficeIds = []; - - foreach ($serviceIds as $serviceId) { - $found = false; - foreach ($requestRelationList as $relation) { - if ($relation->request->id == $serviceId) { - if (!in_array($relation->provider->id, $addedOfficeIds)) { - foreach ($providerList as $provider) { - if ($provider->id == $relation->provider->id) { - $offices[] = [ - "id" => $provider->id, - "name" => $provider->name, - ]; - $addedOfficeIds[] = $provider->id; - $found = true; - break; - } - } - } else { - $found = true; - } - } - } - if (!$found) { - $notFoundIds[] = $serviceId; - } - } - - if (empty($offices)) { - return [ - 'offices' => [], - 'error' => 'Office(s) not found for the provided serviceId(s)', - 'status' => 404 - ]; - } - - $responseContent = ['offices' => $offices]; - if (!empty($notFoundIds)) { - $responseContent['warning'] = 'The following serviceId(s) were not found: ' . implode(', ', $notFoundIds); - } - - return [ - 'offices' => $responseContent, - 'status' => 200 - ]; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php b/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php deleted file mode 100644 index aedf672c8..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/OfficesServicesRelationsService.php +++ /dev/null @@ -1,165 +0,0 @@ -getProviderList() ?? []; - $requestList = $sources->getRequestList() ?? []; - $relationList = $sources->getRequestRelationList() ?? []; - - $offices = $this->mapOfficesWithScope($sources, $providerList); - $services = $this->mapServicesWithCombinations($requestList, $relationList); - $relations = $this->mapRelations($relationList); - - return [ - 'offices' => $offices, - 'services' => $services, - 'relations' => $relations, - ]; - } - - private function mapOfficesWithScope($sources, $providerList) - { - $offices = []; - foreach ($providerList as $provider) { - $officeData = [ - "id" => $provider->id, - "name" => $provider->displayName ?? $provider->name, - ]; - $scope = $this->getScopeForProvider($sources, $provider->id); - if ($scope) { - $officeData['scope'] = $scope; - } - - $offices[] = $officeData; - } - return $offices; - } - - private function mapServicesWithCombinations($requestList, $relationList) - { - $servicesProviderIds = []; - foreach ($relationList as $relation) { - if (!isset($servicesProviderIds[$relation->request->id])) { - $servicesProviderIds[$relation->request->id] = []; - } - $servicesProviderIds[$relation->request->id][] = $relation->provider->id; - } - - $services = []; - foreach ($requestList as $service) { - $serviceCombinations = []; - $mappedService = [ - "id" => $service->getId(), - "name" => $service->getName(), - "maxQuantity" => $service->getAdditionalData()['maxQuantity'] ?? 1, - ]; - - if (isset($service->getAdditionalData()['combinable'])) { - foreach ($service->getAdditionalData()['combinable'] as $combinationServiceId) { - $commonProviders = array_intersect( - $servicesProviderIds[$service->getId()] ?? [], - $servicesProviderIds[$combinationServiceId] ?? [] - ); - $serviceCombinations[$combinationServiceId] = !empty($commonProviders) ? array_values($commonProviders) : []; - } - $mappedService['combinable'] = $serviceCombinations; - } - - $services[] = $mappedService; - } - - return $services; - } - - private function mapRelations($relationList) - { - $relations = []; - foreach ($relationList as $relation) { - $relations[] = [ - "officeId" => $relation->provider->id, - "serviceId" => $relation->request->id, - "slots" => intval($relation->slots) - ]; - } - return $relations; - } - - private function getScopeForProvider($sources, $providerId) - { - $scopeList = $sources->getScopeList(); - foreach ($scopeList as $scope) { - if ($scope->provider->id === $providerId) { - return [ - "id" => $scope->id, - "provider" => $scope->provider, - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), - "displayInfo" => $scope->getDisplayInfo() - ]; - } - } - return null; - } - - - public function validateServiceLocationCombination($officeId, array $serviceIds) - { - // Fetch all available services for the given officeId - $availableServices = $this->getServicesProvidedAtOffice($officeId); - $availableServiceIds = array_map(function ($service) { - return $service['id']; - }, $availableServices); - - // Check if there are any invalid service IDs - $invalidServiceIds = array_filter($serviceIds, function ($serviceId) use ($availableServiceIds) { - return !in_array($serviceId, $availableServiceIds); - }); - - if (!empty($invalidServiceIds)) { - return [ - 'status' => 400, - 'errorCode' => 'invalidLocationAndServiceCombination', - 'errorMessage' => 'The provided service(s) do not exist at the given location.', - 'invalidServiceIds' => $invalidServiceIds, - 'locationId' => $officeId, - 'lastModified' => time() * 1000, - ]; - } - - return [ - 'status' => 200, - 'message' => 'Valid service-location combination.', - ]; - } - - public function getServicesProvidedAtOffice($officeId) - { - $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $requestList = $sources->getRequestList() ?? []; - $requestRelationList = $sources->getRequestRelationList() ?? []; - - $serviceIds = array_filter($requestRelationList, function ($relation) use ($officeId) { - return $relation->provider->id === $officeId; - }); - - $serviceIds = array_map(function ($relation) { - return $relation->request->id; - }, $serviceIds); - - return array_filter($requestList, function ($request) use ($serviceIds) { - return in_array($request->id, $serviceIds); - }); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php deleted file mode 100644 index 1ba0b7125..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ProcessService.php +++ /dev/null @@ -1,110 +0,0 @@ -httpClient = $httpClient; - } - - public function getProcessById($processId, $authKey) - { - $resolveReferences = 2; - $process = $this->httpClient->readGetResult("/process/{$processId}/{$authKey}/", [ - 'resolveReferences' => $resolveReferences - ])->getEntity(); - - return $process; - } - - public function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) - { - $requests = []; - - foreach ($serviceIds as $index => $serviceId) { - $count = intval($serviceCounts[$index]); - for ($i = 0; $i < $count; $i++) { - $requests[] = [ - 'id' => $serviceId, - 'source' => 'dldb' - ]; - } - } - - $appointmentProcess['requests'] = $requests; - - return $this->httpClient->readPostResult('/process/status/reserved/', $appointmentProcess)->getEntity(); - } - - public function submitClientData($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/"; - return $this->httpClient->readPostResult($url, $process)->getEntity(); - } - - public function preconfirmProcess($process) - { - $url = '/process/status/preconfirmed/'; - return $this->httpClient->readPostResult($url, $process)->getEntity(); - } - - public function confirmProcess($process) - { - $url = '/process/status/confirmed/'; - return $this->httpClient->readPostResult($url, $process)->getEntity(); - } - - public function cancelAppointment($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/"; - return $this->httpClient->readDeleteResult($url, $process)->getEntity(); - } - - public function sendConfirmationEmail($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; - return $this->httpClient->readPostResult($url, $process)->getEntity(); - } - - public function sendPreconfirmationEmail($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; - return $this->httpClient->readPostResult($url, $process)->getEntity(); - } - - public function sendCancelationEmail($process) - { - $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; - return $this->httpClient->readPostResult($url, $process)->getEntity(); - } - - public function getFreeDays($providers, $requests, $firstDay, $lastDay) - { - $requestUrl = '/calendar/'; - $dataPayload = [ - 'firstDay' => $firstDay, - 'lastDay' => $lastDay, - 'providers' => $providers, - 'requests' => $requests, - ]; - - return $this->httpClient->readPostResult($requestUrl, $dataPayload)->getEntity(); - } - - public function getFreeTimeslots($providers, $requests, $firstDay, $lastDay) - { - $requestUrl = '/process/status/free/'; - $dataPayload = [ - 'firstDay' => $firstDay, - 'lastDay' => $lastDay, - 'providers' => $providers, - 'requests' => $requests, - ]; - - return $this->httpClient->readPostResult($requestUrl, $dataPayload)->getEntity(); - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php deleted file mode 100644 index 7391a841d..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ScopesService.php +++ /dev/null @@ -1,122 +0,0 @@ -readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $scopeList = $sources->getScopeList() ?? []; - $scopesProjectionList = []; - - foreach ($scopeList as $scope) { - $scopesProjectionList[] = [ - "id" => $scope->id, - "provider" => $scope->provider, - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() - ]; - } - - return $scopesProjectionList; - } - - public function getScopeByIds(array $scopeIds) - { - $scopeIds = array_unique($scopeIds); - - if (empty($scopeIds) || $scopeIds == ['']) { - return [ - 'scopes' => [], - 'error' => 'Invalid scopeId(s)', - 'status' => 400 - ]; - } - - $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $scopeList = $sources->getScopeList(); - $scopes = []; - $notFoundIds = []; - - foreach ($scopeIds as $scopeId) { - $found = false; - foreach ($scopeList as $scopeItem) { - if ($scopeItem->id == $scopeId) { - $scopes[] = [ - "id" => $scopeItem->id, - "provider" => [ - "id" => $scopeItem->provider->id, - "source" => $scopeItem->provider->source, - ], - "shortName" => $scopeItem->shortName, - "telephoneActivated" => $scopeItem->getTelephoneActivated(), - "telephoneRequired" => $scopeItem->getTelephoneRequired(), - "customTextfieldActivated" => $scopeItem->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scopeItem->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scopeItem->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scopeItem->getCaptchaActivatedRequired() - ]; - $found = true; - break; - } - } - - if (!$found) { - $notFoundIds[] = $scopeId; - } - } - - if (empty($scopes)) { - return [ - 'scopes' => [], - 'error' => 'Scope(s) not found', - 'status' => 404 - ]; - } - - $responseContent = ['scopes' => $scopes]; - if (!empty($notFoundIds)) { - $responseContent['warning'] = 'The following scopeId(s) were not found: ' . implode(', ', $notFoundIds); - } - - return [ - 'scopes' => $responseContent, - 'status' => 200 - ]; - } - - public function getScopeByOfficeId($officeId) - { - try { - $scopes = $this->getScopes(); - - foreach ($scopes as $scope) { - if (isset($scope['provider']) && $scope['provider']->id == $officeId) { - return $scope; - } - } - - return [ - 'error' => 'Scope not found for the provided office ID', - 'status' => 404 - ]; - } catch (\Exception $e) { - return [ - 'error' => 'Error fetching scope by office ID: ' . $e->getMessage(), - 'status' => 500 - ]; - } - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php deleted file mode 100644 index 9a90705c8..000000000 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ServicesService.php +++ /dev/null @@ -1,91 +0,0 @@ -readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $requestList = $sources->getRequestList() ?? []; - $servicesProjectionList = []; - - foreach ($requestList as $request) { - $additionalData = $request->getAdditionalData(); - $servicesProjectionList[] = [ - "id" => $request->getId(), - "name" => $request->getName(), - "maxQuantity" => $additionalData['maxQuantity'] ?? 1, - ]; - } - - return $servicesProjectionList; - } - - public function getServicesByOfficeIds(array $officeIds) - { - $officeIds = array_unique($officeIds); - - if (empty($officeIds) || $officeIds == ['']) { - return [ - 'services' => [], - 'error' => 'Invalid officeId(s)', - 'status' => 400, - ]; - } - - $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ - 'resolveReferences' => 2, - ])->getEntity(); - - $requestList = $sources->getRequestList(); - $requestRelationList = $sources->getRequestRelationList(); - - $services = []; - $notFoundIds = []; - $addedServices = []; - - foreach ($officeIds as $officeId) { - $found = false; - foreach ($requestRelationList as $relation) { - if ($relation->provider->id == $officeId) { - foreach ($requestList as $request) { - if ($request->id == $relation->request->id && !in_array($request->id, $addedServices)) { - $services[] = [ - "id" => $request->id, - "name" => $request->name, - "maxQuantity" => $request->getAdditionalData()['maxQuantity'] ?? 1, - ]; - $addedServices[] = $request->id; - $found = true; - } - } - } - } - if (!$found) { - $notFoundIds[] = $officeId; - } - } - - if (empty($services)) { - return [ - 'services' => [], - 'error' => 'Service(s) not found for the provided officeId(s)', - 'status' => 404, - ]; - } - - $responseContent = ['services' => $services]; - if (!empty($notFoundIds)) { - $responseContent['warning'] = 'The following officeId(s) were not found: ' . implode(', ', $notFoundIds); - } - - return [ - 'services' => $responseContent, - 'status' => 200, - ]; - } -} diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php new file mode 100644 index 000000000..91b6c01fc --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -0,0 +1,313 @@ + 400, + 'errorCode' => 'invalidLocationAndServiceCombination', + 'errorMessage' => 'The provided service(s) do not exist at the given location.', + 'invalidServiceIds' => $invalidServiceIds, + 'locationId' => $officeId + ]; + } + + return [ + 'status' => 200, + 'message' => 'Valid service-location combination.', + ]; + } + + public static function validateGetBookableFreeDays($officeId, $serviceId, $startDate, $endDate, $serviceCounts) + { + $errors = []; + if (!$startDate) { + $errors[] = ['status' => 400, 'errorMessage' => 'startDate is required and must be a valid date.']; + } + if (!$endDate) { + $errors[] = ['status' => 400, 'errorMessage' => 'endDate is required and must be a valid date.']; + } + if (!$officeId || !is_numeric($officeId)) { + $errors[] = ['status' => 400, 'errorMessage' => 'officeId should be a 32-bit integer.']; + } + if (!$serviceId || !is_numeric($serviceId)) { + $errors[] = ['status' => 400, 'errorMessage' => 'serviceId should be a 32-bit integer.']; + } + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { + $errors[] = ['status' => 400, 'errorMessage' => 'serviceCount should be a comma-separated string of integers.']; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetProcessById($processId, $authKey) + { + $errors = []; + if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'processId should be a 32-bit integer.', + ]; + } + + if (!$authKey || !is_string($authKey)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'authKey should be a string.', + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetAvailableAppointments($date, $officeId, $serviceIds, $serviceCounts) + { + $errors = []; + if (!$date) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'date is required and must be a valid date.', + ]; + } + + if (!$officeId || !is_numeric($officeId)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'officeId should be a 32-bit integer.', + ]; + } + + if (empty($serviceIds[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceIds))) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + ]; + } + + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + public static function validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $captchaSolution, $timestamp) + { + $errors = []; + if (!$officeId) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Missing officeId.', + ]; + } elseif (!is_numeric($officeId)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Invalid officeId format. It should be a numeric value.', + ]; + } + + if (empty($serviceIds)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Missing serviceId.', + ]; + } elseif (!is_array($serviceIds) || array_filter($serviceIds, fn($id) => !is_numeric($id))) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Invalid serviceId format. It should be an array of numeric values.', + ]; + } + + if (!$timestamp) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Missing timestamp.', + ]; + } elseif (!is_numeric($timestamp) || $timestamp < 0) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Invalid timestamp format. It should be a positive numeric value.', + ]; + } + + if (!is_array($serviceCounts) || array_filter($serviceCounts, fn($count) => !is_numeric($count) || $count < 0)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'Invalid serviceCount format. It should be an array of non-negative numeric values.', + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetOfficesByServiceIds($serviceIds) + { + $errors = []; + if (empty($serviceIds) || $serviceIds == ['']) { + $errors[] = [ + 'offices' => [], + 'errorMessage' => 'Invalid serviceId(s).', + 'status' => 400 + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetScopeByIds($scopeIds) + { + $errors = []; + if (empty($scopeIds) || $scopeIds == ['']) { + $errors[] = [ + 'scopes' => [], + 'errorMessage' => 'Invalid scopeId(s).', + 'status' => 400 + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetServicesByOfficeIds($officeIds){ + + $errors = []; + if (empty($officeIds) || $officeIds == ['']) { + $errors[] = [ + 'services' => [], + 'errorMessage' => 'Invalid officeId(s)', + 'status' => 400, + ]; + } + + return ['errors' => $errors, 'status' => 400]; + } + + public static function validateGetProcessFreeSlots($freeSlots) + { + $errors = []; + if (empty($freeSlots) || !is_iterable($freeSlots)) { + $errors[] = [ + 'appointmentTimestamps' => [], + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateGetProcessByIdTimestamps($appointmentTimestamps) + { + $errors = []; + if (empty($appointmentTimestamps)) { + $errors[] = [ + 'appointmentTimestamps' => [], + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateGetProcessNotFound($process) + { + $errors = []; + if (!$process) { + $errors[] = [ + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateScopesNotFound($scopes) + { + $errors = []; + if (empty($scopes)) { + $errors[] = [ + 'errorCode' => 'scopesNotFound', + 'errorMessage' => 'Scope(s) not found.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateServicesNotFound($services) + { + $errors = []; + if (empty($services)) { + $errors[] = [ + 'errorCode' => 'servicesNotFound', + 'errorMessage' => 'Service(s) not found for the provided officeId(s).', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateOfficesNotFound($offices) + { + $errors = []; + if (empty($offices)) { + $errors[] = [ + 'errorCode' => 'officesNotFound', + 'errorMessage' => 'Office(s) not found for the provided serviceId(s).', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateAppointmentDaysNotFound($formattedDays) + { + $errors = []; + if (empty($formattedDays)) { + $errors[] = [ + 'errorCode' => 'noAppointmentForThisDay', + 'errorMessage' => 'No available days found for the given criteria.', + 'status' => 404, + ]; + } + + return ['errors' => $errors, 'status' => 404]; + } + + public static function validateNoAppointmentsAtLocation(){ + + $errors[] = [ + 'errorCode' => 'noAppointmentForThisScope', + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', + 'status' => 404, + ]; + + return ['errors' => $errors, 'status' => 404]; + + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php new file mode 100644 index 000000000..4f5d0519e --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -0,0 +1,186 @@ +readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $providerList = $sources->getProviderList() ?? []; + + return $providerList; + + } + + public static function getScopes() + { + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $scopeList = $sources->getScopeList() ?? []; + + return $scopeList; + + } + + public static function getServices() + { + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $requestList = $sources->getRequestList() ?? []; + + return $requestList; + + } + + public static function getRequestRelationList(){ + + $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ + 'resolveReferences' => 2, + ])->getEntity(); + + $requestRelationList = $sources->getRequestRelationList(); + + return $requestRelationList; + } + + /* Todo add cache methods + haveCachedSourcesExpired + getSources + + */ + + public static function getFreeDays($providers, $requests, $firstDay, $lastDay) + { + + $calendar = new CalendarEntity(); + $calendar->firstDay = $firstDay; + $calendar->lastDay = $lastDay; + $calendar->providers = $providers; + $calendar->requests = $requests; + + return \App::$http->readPostResult('/calendar/', $calendar)->getEntity(); + } + + public static function getFreeTimeslots($providers, $requests, $firstDay, $lastDay) + { + + $calendar = new CalendarEntity(); + $calendar->firstDay = $firstDay; + $calendar->lastDay = $lastDay; + $calendar->providers = $providers; + $calendar->requests = $requests; + + + $result = \App::$http->readPostResult('/process/status/free/', $calendar); + if (!$result || !method_exists($result, 'getCollection')) { + throw new \Exception('Invalid response from API'); + } + + $psr7Response = $result->getResponse(); + $responseBody = (string) $psr7Response->getBody(); + + return $result; + } + + public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) + { + $requests = []; + + foreach ($serviceIds as $index => $serviceId) { + $count = intval($serviceCounts[$index]); + for ($i = 0; $i < $count; $i++) { + $requests[] = [ + 'id' => $serviceId, + 'source' => 'dldb' + ]; + } + } + + $processEntity = new ProcessEntity(); + + $processEntity->appointments = $appointmentProcess['appointments'] ?? []; + $processEntity->authKey = $appointmentProcess['authKey'] ?? null; + $processEntity->clients = $appointmentProcess['clients'] ?? []; + + $processEntity->scope = $appointmentProcess['scope'] ?? null; + $processEntity->requests = $requests; + $processEntity->lastChange = $appointmentProcess['lastChange'] ?? time(); + + + $processEntity->createIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; + $processEntity->createTimestamp = time(); + + if (isset($appointmentProcess['queue'])) { + $processEntity->queue = $appointmentProcess['queue']; + } + + $result = \App::$http->readPostResult('/process/status/reserved/', $processEntity); + + return $result->getEntity(); + } + + public function submitClientData($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function preconfirmProcess($process) + { + $url = '/process/status/preconfirmed/'; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function confirmProcess($process) + { + $url = '/process/status/confirmed/'; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function cancelAppointment($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/"; + return \App::$http->readDeleteResult($url, $process)->getEntity(); + } + + public function sendConfirmationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function sendPreconfirmationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public function sendCancelationEmail($process) + { + $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; + return \App::$http->readPostResult($url, $process)->getEntity(); + } + + public static function getProcessById($processId, $authKey) + { + $resolveReferences = 2; + $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ + 'resolveReferences' => $resolveReferences + ])->getEntity(); + + return $process; + } + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php new file mode 100644 index 000000000..4f23c0597 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -0,0 +1,641 @@ +provider->id == $provider->id) { + $matchingScope = $scope; + break; + } + } + + $providerData = [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ]; + + if ($matchingScope) { + $providerData["scope"] = [ + "id" => $matchingScope->id, + "provider" => $matchingScope->provider, + "shortName" => $matchingScope->shortName, + "telephoneActivated" => $matchingScope->getTelephoneActivated(), + "telephoneRequired" => $matchingScope->getTelephoneRequired(), + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), + "displayInfo" => $matchingScope->getDisplayInfo() + ]; + } + + $providerProjectionList[] = $providerData; + } + + return [ + "offices" => $providerProjectionList, + "status" => 200 + ]; + } + + + public static function getScopes() + { + $scopeList = ZmsApiClientService::getScopes() ?? []; + $scopesProjectionList = []; + + foreach ($scopeList as $scope) { + $scopesProjectionList[] = [ + "id" => $scope->id, + "provider" => $scope->provider, + "shortName" => $scope->shortName, + "telephoneActivated" => $scope->getTelephoneActivated(), + "telephoneRequired" => $scope->getTelephoneRequired(), + "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), + "displayInfo" => $scope->getDisplayInfo() + ]; + } + + return [ + "scopes" => $scopesProjectionList, + "status" => 200 + ]; + } + + public static function getServices() + { + + $requestList = ZmsApiClientService::getServices() ?? []; + $servicesProjectionList = []; + + foreach ($requestList as $request) { + $additionalData = $request->getAdditionalData(); + $servicesProjectionList[] = [ + "id" => $request->getId(), + "name" => $request->getName(), + "maxQuantity" => $additionalData['maxQuantity'] ?? 1, + ]; + } + + return [ + "services" => $servicesProjectionList, + "status" => 200 + ]; + } + + public static function getScopeForProvider($providerId) + { + $scopeList = ZmsApiClientService::getScopes() ?? []; + foreach ($scopeList as $scope) { + if ($scope->provider->id === $providerId) { + return [ + "id" => $scope->id, + "provider" => $scope->provider, + "shortName" => $scope->shortName, + "telephoneActivated" => $scope->getTelephoneActivated(), + "telephoneRequired" => $scope->getTelephoneRequired(), + "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), + "displayInfo" => $scope->getDisplayInfo() + ]; + } + } + return null; + } + + public static function getServicesAndOffices() + { + $providerList = ZmsApiClientService::getOffices() ?? []; + $requestList = ZmsApiClientService::getServices() ?? []; + $relationList = ZmsApiClientService::getRequestRelationList() ?? []; + + $offices = MapperService::mapOfficesWithScope($providerList); + $services = MapperService::mapServicesWithCombinations($requestList, $relationList); + $relations = MapperService::mapRelations($relationList); + + return [ + "offices" => $offices, + "services" => $services, + "relations" => $relations, + "status" => 200 + ]; + } + + /* Todo add method + * getCombinableServicesByIds + * + * + * + */ + + public static function getScopeByOfficeId($officeId) + { + try { + $scopes = self::getScopes(); + + foreach ($scopes as $scope) { + if (isset($scope['provider']) && $scope['provider']->id == $officeId) { + return $scope; + } + } + + return [ + 'error' => 'Scope not found for the provided office ID', + 'status' => 404 + ]; + } catch (\Exception $e) { + return [ + 'error' => 'Error fetching scope by office ID: ' . $e->getMessage(), + 'status' => 500 + ]; + } + } + + /* Todo add method + * getOfficeByIds + * + * + * + */ + + public static function getOfficesByServiceIds(array $serviceIds) + { + $serviceIds = array_unique($serviceIds); + + $errors = ValidationService::validateGetOfficesByServiceIds($serviceIds); + if (!empty($errors['errors'])) { + return $errors; + } + + $providerList = ZmsApiClientService::getOffices(); + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $offices = []; + $notFoundIds = []; + $addedOfficeIds = []; + + foreach ($serviceIds as $serviceId) { + $found = false; + foreach ($requestRelationList as $relation) { + if ($relation->request->id == $serviceId) { + if (!in_array($relation->provider->id, $addedOfficeIds)) { + foreach ($providerList as $provider) { + if ($provider->id == $relation->provider->id) { + $offices[] = [ + "id" => $provider->id, + "name" => $provider->name, + ]; + $addedOfficeIds[] = $provider->id; + $found = true; + break; + } + } + } else { + $found = true; + } + } + } + if (!$found) { + $notFoundIds[] = $serviceId; + } + } + + $errors = ValidationService::validateOfficesNotFound($offices); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseContent = ['offices' => $offices]; + if (!empty($notFoundIds)) { + $responseContent['warning'] = 'The following serviceId(s) were not found: ' . implode(', ', $notFoundIds); + } + + return [ + 'offices' => $responseContent, + 'status' => 200 + ]; + } + + public static function getScopeByIds(array $scopeIds) + { + $scopeIds = array_unique($scopeIds); + + $errors = ValidationService::validateGetScopeByIds($scopeIds); + if (!empty($errors['errors'])) { + return $errors; + } + + $scopeList = ZmsApiClientService::getScopes() ?? []; + $scopes = []; + $notFoundIds = []; + + foreach ($scopeIds as $scopeId) { + $found = false; + foreach ($scopeList as $scopeItem) { + if ($scopeItem->id == $scopeId) { + $scopes[] = [ + "id" => $scopeItem->id, + "provider" => [ + "id" => $scopeItem->provider->id, + "source" => $scopeItem->provider->source, + ], + "shortName" => $scopeItem->shortName, + "telephoneActivated" => $scopeItem->getTelephoneActivated(), + "telephoneRequired" => $scopeItem->getTelephoneRequired(), + "customTextfieldActivated" => $scopeItem->getCustomTextfieldActivated(), + "customTextfieldRequired" => $scopeItem->getCustomTextfieldRequired(), + "customTextfieldLabel" => $scopeItem->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $scopeItem->getCaptchaActivatedRequired(), + "displayInfo" => $scopeItem->getDisplayInfo() + ]; + $found = true; + break; + } + } + + if (!$found) { + $notFoundIds[] = $scopeId; + } + } + + $errors = ValidationService::validateScopesNotFound($scopes); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseContent = ['scopes' => $scopes]; + if (!empty($notFoundIds)) { + $responseContent['warning'] = 'The following scopeId(s) were not found: ' . implode(', ', $notFoundIds); + } + + return [ + 'scopes' => $responseContent, + 'status' => 200 + ]; + } + + public static function getServicesByOfficeIds(array $officeIds) + { + $officeIds = array_unique($officeIds); + + $errors = ValidationService::validateGetServicesByOfficeIds($officeIds); + if (!empty($errors['errors'])) { + return $errors; + } + + $requestList = ZmsApiClientService::getServices() ?? []; + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $services = []; + $notFoundIds = []; + $addedServices = []; + + foreach ($officeIds as $officeId) { + $found = false; + foreach ($requestRelationList as $relation) { + if ($relation->provider->id == $officeId) { + foreach ($requestList as $request) { + if ($request->id == $relation->request->id && !in_array($request->id, $addedServices)) { + $services[] = [ + "id" => $request->id, + "name" => $request->name, + "maxQuantity" => $request->getAdditionalData()['maxQuantity'] ?? 1, + ]; + $addedServices[] = $request->id; + $found = true; + } + } + } + } + if (!$found) { + $notFoundIds[] = $officeId; + } + } + + $errors = ValidationService::validateServicesNotFound($services); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseContent = ['services' => $services]; + if (!empty($notFoundIds)) { + $responseContent['warning'] = 'The following officeId(s) were not found: ' . implode(', ', $notFoundIds); + } + + return [ + 'services' => $responseContent, + 'status' => 200, + ]; + } + + /* Todo add method + * getOfficesThatProvideService + * + * + * + */ + + public static function getServicesProvidedAtOffice($officeId) + { + + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $requestRelationArray = []; + foreach ($requestRelationList as $relation) { + $requestRelationArray[] = $relation; + } + + $serviceIds = array_filter($requestRelationArray, function ($relation) use ($officeId) { + return $relation->provider->id === $officeId; + }); + + $serviceIds = array_map(function ($relation) { + return $relation->request->id; + }, $serviceIds); + + $requestList = ZmsApiClientService::getServices() ?? []; + $requestArray = []; + foreach ($requestList as $request) { + $requestArray[] = $request; + } + + return array_filter($requestArray, function ($request) use ($serviceIds) { + return in_array($request->id, $serviceIds); + }); + } + + public static function getBookableFreeDays(array $queryParams) + { + $officeId = $queryParams['officeId'] ?? null; + $serviceId = $queryParams['serviceId'] ?? null; + $serviceCounts = isset($queryParams['serviceCount']) ? explode(',', $queryParams['serviceCount']) : []; + $startDate = $queryParams['startDate'] ?? null; + $endDate = $queryParams['endDate'] ?? null; + + $errors = ValidationService::validateGetBookableFreeDays($officeId, $serviceId, $startDate, $endDate, $serviceCounts); + if (!empty($errors['errors'])) { + return $errors; + } + + try { + $firstDay = UtilityHelper::getInternalDateFromISO($startDate); + $lastDay = UtilityHelper::getInternalDateFromISO($endDate); + + $freeDays = ZmsApiClientService::getFreeDays( + [['id' => $officeId, 'source' => 'dldb']], + [ + [ + 'id' => $serviceId, + 'source' => 'dldb', + 'slotCount' => $serviceCounts, + ] + ], + $firstDay, + $lastDay, + ); + + $daysCollection = $freeDays->days; + $formattedDays = []; + + foreach ($daysCollection as $day) { + $formattedDays[] = sprintf('%04d-%02d-%02d', $day->year, $day->month, $day->day); + } + + $errors = ValidationService::validateAppointmentDaysNotFound($formattedDays); + if (!empty($errors['errors'])) { + return $errors; + } + + return [ + 'availableDays' => $formattedDays, + 'status' => 200, + ]; + + } catch (\Exception $e) { + //error_log('Error in AvailableDaysService: ' . $e->getMessage()); + return ExceptionService::exceptionNoAppointmentsAtLocation(); + } + } + public static function getFreeAppointments(array $params) + { + $office = [ + 'id' => $params['officeId'], + 'source' => 'dldb' + ]; + + $requests = []; + + foreach ($params['serviceIds'] as $index => $serviceId) { + $service = [ + 'id' => $serviceId, + 'source' => 'dldb', + 'slotCount' => $params['serviceCounts'][$index] + ]; + $requests = array_merge($requests, array_fill(0, $service['slotCount'], $service)); + } + + try { + + $freeSlots = ZmsApiClientService::getFreeTimeslots( + [$office], + $requests, + $params['date'], + $params['date'] + ); + + $psr7Response = $freeSlots->getResponse(); + $responseBody = (string) $psr7Response->getBody(); + + $responseBody = json_decode($responseBody, true); + + return $responseBody['data']; + + } catch (\Exception $e) { + //error_log('Error in AvailableAppointmentsService: ' . $e->getMessage()); + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An error occurred while fetching available appointments', + 'status' => 500, + ]; + } + } + + public static function getAvailableAppointments(array $queryParams) + { + $date = $queryParams['date'] ?? null; + $officeId = $queryParams['officeId'] ?? null; + $serviceIds = isset($queryParams['serviceId']) ? explode(',', $queryParams['serviceId']) : []; + $serviceCounts = isset($queryParams['serviceCount']) ? explode(',', $queryParams['serviceCount']) : []; + + $errors = ValidationService::validateGetAvailableAppointments($date, $officeId, $serviceIds, $serviceCounts); + if (!empty($errors['errors'])) { + return $errors; + } + + try { + $requests = []; + foreach ($serviceIds as $index => $serviceId) { + $slotCount = isset($serviceCounts[$index]) ? intval($serviceCounts[$index]) : 1; + for ($i = 0; $i < $slotCount; $i++) { + $requests[] = [ + 'id' => $serviceId, + 'source' => 'dldb', + 'slotCount' => 1, + ]; + } + } + + $freeSlots = ZmsApiClientService::getFreeTimeslots( + [['id' => $officeId, 'source' => 'dldb']], + $requests, + UtilityHelper::getInternalDateFromISO($date), + UtilityHelper::getInternalDateFromISO($date) + ); + + if (!$freeSlots || !method_exists($freeSlots, 'getCollection')) { + throw new \Exception('Invalid response from API'); + } + + return self::processFreeSlots($freeSlots->getCollection()); + + } catch (\Exception $e) { + return [ + 'appointmentTimestamps' => [], + 'errorCode' => 'internalError', + 'errorMessage' => 'An error occurred while fetching available appointments', + 'status' => 500, + ]; + } + } + + private static function processFreeSlots($freeSlots) + { + + $errors = ValidationService::validateGetProcessFreeSlots($freeSlots); + if (!empty($errors['errors'])) { + return $errors; + } + + $currentTimestamp = time(); + $appointmentTimestamps = []; + + foreach ($freeSlots as $slot) { + if (!isset($slot->appointments) || !is_iterable($slot->appointments)) { + continue; + } + + foreach ($slot->appointments as $appointment) { + + if (isset($appointment->date)) { + $timestamp = (int) $appointment->date; + + if (!in_array($timestamp, $appointmentTimestamps) && $timestamp > $currentTimestamp) { + $appointmentTimestamps[] = $timestamp; + } + } + } + } + + $errors = ValidationService::validateGetProcessByIdTimestamps($appointmentTimestamps); + if (!empty($errors['errors'])) { + return $errors; + } + + sort($appointmentTimestamps); + + return [ + 'appointmentTimestamps' => $appointmentTimestamps, + 'status' => 200, + ]; + } + + public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) + { + return ZmsApiClientService::reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts); + } + + public static function getProcessById($processId, $authKey) + { + $errors = ValidationService::validateGetProcessById($processId, $authKey); + if (!empty($errors['errors'])) { + return $errors; + } + + try { + $process = ZmsApiClientService::getProcessById($processId, $authKey); + + + $errors = ValidationService::validateGetProcessNotFound($process); + if (!empty($errors['errors'])) { + return $errors; + } + + $responseData = UtilityHelper::getThinnedProcessData($process); + return ['data' => $responseData, 'status' => 200]; + + } catch (\Exception $e) { + if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { + return [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ]; + } else { + return [ + 'errorCode' => 'unexpectedError', + 'errorMessage' => 'Unexpected error: ' . $e->getMessage(), + 'status' => 500, + ]; + } + } + } + + + /* Todo add method + * updateClientData + * + * + * + */ + + /* Todo add method + * preconfirmAppointment + * + * + * + */ + + + /* Todo add method + * confirmAppointment + * + * + * + */ + + /* Todo add method + * cancelAppointment + * + * + * + */ + +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php similarity index 62% rename from zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php rename to zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php index c3401f811..1c1812758 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Controllers/ServicesByOfficeList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php @@ -1,28 +1,22 @@ servicesService = new ServicesService(); - } public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $officeIds = explode(',', $request->getQueryParams()['officeId'] ?? ''); - $result = $this->servicesService->getServicesByOfficeIds($officeIds); + $result = ZmsApiFacadeService::getServicesByOfficeIds($officeIds); - if (isset($result['error'])) { + if (isset($result['errors'])) { return $this->createJsonResponse($response, $result, $result['status']); } diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php new file mode 100644 index 000000000..a57adc64f --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php @@ -0,0 +1,18 @@ +createJsonResponse($response, $services, statusCode: $services['status']); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php index 0f9264589..a38e2e30b 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php @@ -5,7 +5,7 @@ class AppointmentCancelTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentCancel"; + protected $classname = "AppointmentCancel"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php index 1a9b3f917..04d3d9e25 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php @@ -4,7 +4,7 @@ class AppointmentConfirmTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentConfirm"; + protected $classname = "AppointmentConfirm"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php index 3710bcc3a..858ddc948 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php @@ -6,7 +6,7 @@ class AppointmentGetTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentGet"; + protected $classname = "AppointmentGet"; public function testRendering() { @@ -30,45 +30,132 @@ public function testRendering() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'processId' => '101002', + 'timestamp' => 1724907600, + 'authKey' => 'fb43', + 'familyName' => 'Doe', + 'customTextfield' => '', + 'email' => 'johndoe@example.com', + 'telephone' => '0123456789', + 'officeName' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'officeId' => '102522', + 'scope' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/scope.json', + 'id' => '64', + 'source' => 'dldb', + 'contact' => [ + 'name' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'street' => 'Orleansstraße 50', + 'email' => '', + 'country' => 'Germany' + ], + 'provider' => [ + 'contact' => [ + 'city' => 'Muenchen', + 'country' => 'Germany', + 'name' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'postalCode' => '81667', + 'region' => 'Muenchen', + 'street' => 'Orleansstraße', + 'streetNumber' => '50' + ], + 'id' => '102522', + 'link' => 'https://service.berlin.de/standort/102522/', + 'name' => 'Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', + 'displayName' => 'Bürgerbüro Orleansplatz DEV', + 'source' => 'dldb' + ], + 'hint' => '', + 'lastChange' => 1724192287, + 'preferences' => [ + 'appointment' => [ + 'deallocationDuration' => '15', + 'endInDaysDefault' => '60', + 'multipleSlotsEnabled' => '0', + 'reservationDuration' => '15', + 'activationDuration' => '15', + 'startInDaysDefault' => '2', + 'notificationConfirmationEnabled' => '0', + 'notificationHeadsUpEnabled' => '0' + ], + 'client' => [ + 'alternateAppointmentUrl' => '', + 'amendmentActivated' => '0', + 'amendmentLabel' => '', + 'emailFrom' => 'terminvereinbarung@muenchen.de', + 'emailRequired' => '0', + 'telephoneActivated' => '1', + 'telephoneRequired' => '1', + 'appointmentsPerMail' => '1', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '1', + 'customTextfieldLabel' => 'Nachname des Kindes', + 'captchaActivatedRequired' => '0', + 'adminMailOnAppointment' => 0, + 'adminMailOnDeleted' => 0, + 'adminMailOnUpdated' => 0, + 'adminMailOnMailSent' => 0 + ], + 'notifications' => [ + 'confirmationContent' => '', + 'headsUpContent' => '', + 'headsUpTime' => '10' + ], + 'pickup' => [ + 'alternateName' => 'Ausgabe', + 'isDefault' => '0' + ], + 'queue' => [ + 'callCountMax' => '1', + 'callDisplayText' => 'Herzlich Willkommen', + 'firstNumber' => '1', + 'lastNumber' => '999', + 'maxNumberContingent' => '999', + 'processingTimeAverage' => '12', + 'publishWaitingTimeEnabled' => '0', + 'statisticsEnabled' => '0' + ], + 'survey' => [ + 'emailContent' => '', + 'enabled' => '0', + 'label' => '' + ], + 'ticketprinter' => [ + 'buttonName' => 'Bürgerbüro Orleansplatz (KVR-II/231)', + 'confirmationEnabled' => '0', + 'deactivatedText' => 'dasdsa', + 'notificationsAmendmentEnabled' => '0', + 'notificationsEnabled' => '0', + 'notificationsDelay' => '0' + ], + 'workstation' => [ + 'emergencyEnabled' => '0', + 'emergencyRefreshInterval' => '5' + ] + ], + 'shortName' => 'DEVV', + 'status' => [ + 'emergency' => [ + 'activated' => '0' + ], + 'queue' => [ + 'ghostWorkstationCount' => '-1', + 'givenNumberCount' => '4', + 'lastGivenNumber' => '4', + 'lastGivenNumberTimestamp' => 1715292000 + ], + 'ticketprinter' => [ + 'deactivated' => '0' + ] + ] + ], + 'subRequestCounts' => [], + 'serviceId' => '1063424', + 'serviceCount' => 1 + ]; $this->assertEquals(200, $response->getStatusCode()); - - $this->assertArrayHasKey('processId', $responseBody); - $this->assertEquals('101002', $responseBody['processId']); - - $this->assertArrayHasKey('timestamp', $responseBody); - $this->assertEquals(1724907600, $responseBody['timestamp']); - - $this->assertArrayHasKey('authKey', $responseBody); - $this->assertEquals('fb43', $responseBody['authKey']); - - $this->assertArrayHasKey('familyName', $responseBody); - $this->assertEquals('Doe', $responseBody['familyName']); - - $this->assertArrayHasKey('email', $responseBody); - $this->assertEquals('johndoe@example.com', $responseBody['email']); - - $this->assertArrayHasKey('telephone', $responseBody); - $this->assertEquals('0123456789', $responseBody['telephone']); - - $this->assertArrayHasKey('officeName', $responseBody); - $this->assertEquals('Bürgerbüro Orleansplatz DEV (KVR-II/231 DEV)', $responseBody['officeName']); - - $this->assertArrayHasKey('officeId', $responseBody); - $this->assertEquals('102522', $responseBody['officeId']); - - $this->assertArrayHasKey('scope', $responseBody); - $this->assertArrayHasKey('id', $responseBody['scope']); - $this->assertEquals('64', $responseBody['scope']['id']); - - $this->assertArrayHasKey('serviceId', $responseBody); - $this->assertEquals('1063424', $responseBody['serviceId']); - - $this->assertArrayHasKey('serviceCount', $responseBody); - $this->assertEquals(1, $responseBody['serviceCount']); - - $this->assertArrayHasKey('subRequestCounts', $responseBody); - $this->assertEmpty($responseBody['subRequestCounts']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testMissingProcessId() @@ -79,10 +166,17 @@ public function testMissingProcessId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'processId should be a 32-bit integer.' + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertEquals('processId should be a 32-bit integer', $responseBody['errors'][0]['msg']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testMissingAuthKey() @@ -93,10 +187,17 @@ public function testMissingAuthKey() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'authKey should be a string.' + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertEquals('authKey should be a string', $responseBody['errors'][0]['msg']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testInvalidProcessId() @@ -108,10 +209,17 @@ public function testInvalidProcessId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'processId should be a 32-bit integer.' + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertEquals('processId should be a 32-bit integer', $responseBody['errors'][0]['msg']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testInvalidAuthKey() @@ -123,10 +231,17 @@ public function testInvalidAuthKey() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'authKey should be a string.' + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertEquals('authKey should be a string', $responseBody['errors'][0]['msg']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testBothParametersMissing() @@ -135,32 +250,22 @@ public function testBothParametersMissing() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - - $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $errors = $responseBody['errors']; - - $this->assertCount(2, $errors); - - $this->assertContains( - [ - 'type' => 'field', - 'msg' => 'processId should be a 32-bit integer', - 'path' => 'processId', - 'location' => 'query' + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'processId should be a 32-bit integer.', + ], + [ + 'status' => 400, + 'errorMessage' => 'authKey should be a string.', + ] ], - $errors - ); + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); - $this->assertContains( - [ - 'type' => 'field', - 'msg' => 'authKey should be a string', - 'path' => 'authKey', - 'location' => 'query' - ], - $errors - ); } public function testAppointmentNotFound() @@ -177,18 +282,21 @@ public function testAppointmentNotFound() ] ] ); - + $parameters = [ 'processId' => '101002', 'authKey' => 'fb43', ]; - + $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); - + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ]; $this->assertEquals(404, $response->getStatusCode()); - - $this->assertArrayHasKey('errorMessage', $responseBody); - $this->assertEquals('Termin wurde nicht gefunden', $responseBody['errorMessage']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } + } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php index 4d65b967d..3208be31f 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php @@ -5,7 +5,7 @@ class AppointmentPreconfirmTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentPreconfirm"; + protected $classname = "AppointmentPreconfirm"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php index ec999ccb0..45dfe6e0c 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -2,13 +2,450 @@ namespace BO\Zmscitizenapi\Tests; +use Psr\Http\Message\ResponseInterface; + class AppointmentReserveTest extends Base { + protected $classname = "AppointmentReserve"; + + public function testRendering() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_reserve_SourceGet_dldb.json"), + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/status/free/', + 'response' => $this->readFixture("GET_appointments_free.json") + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/status/reserved/', + 'response' => $this->readFixture("POST_reserve_timeslot.json") + ] + ] + ); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'processId' => '101142', + 'timestamp' => 32526616522, + 'authKey' => 'b93e', + 'familyName' => '', + 'customTextfield' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '', + 'officeName' => null, + 'officeId' => '10546', + 'scope' => [ + 'id' => '58', + 'provider' => [ + 'id' => '10546', + 'source' => 'dldb' + ], + 'shortName' => 'Gewerbemeldungen', + 'telephoneActivated' => '0', + 'telephoneRequired' => '1', + 'customTextfieldActivated' => '0', + 'customTextfieldRequired' => '1', + 'customTextfieldLabel' => '', + 'captchaActivatedRequired' => '0', + 'displayInfo' => null + ], + 'subRequestCounts' => [], + 'serviceId' => null, + 'serviceCount' => 0 + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testAppointmentNotAvailable() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/source/unittest/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_reserve_SourceGet_dldb.json"), + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/status/free/', + 'response' => $this->readFixture("GET_appointments_free.json") + ] + ] + ); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616300", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'appointmentNotAvailable', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testMissingOfficeId() + { + $this->setApiCalls([]); + + $parameters = [ + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing officeId.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testMissingServiceId() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing serviceId.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testMissingTimestamp() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing timestamp.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testMissingOfficeIdAndServiceId() + { + $this->setApiCalls([]); - protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentReserve"; + $parameters = [ + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; - public function testRendering() { - $responseData = $this->renderJson(method: 'POST'); - $this->assertEqualsCanonicalizing([], $responseData); + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing officeId.', + ], + [ + 'status' => 400, + 'errorMessage' => 'Missing serviceId.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } + + public function testMissingOfficeIdAndTimestamp() + { + $this->setApiCalls([]); + + $parameters = [ + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing officeId.', + ], + [ + 'status' => 400, + 'errorMessage' => 'Missing timestamp.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testMissingServiceIdAndTimestamp() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceCount' => [0], + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing serviceId.', + ], + [ + 'status' => 400, + 'errorMessage' => 'Missing timestamp.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testMissingAllFields() + { + $this->setApiCalls([]); + + $parameters = []; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing officeId.', + ], + [ + 'status' => 400, + 'errorMessage' => 'Missing serviceId.', + ], + [ + 'status' => 400, + 'errorMessage' => 'Missing timestamp.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testInvalidOfficeIdFormat() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => 'invalid_id', + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Invalid officeId format. It should be a numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testInvalidServiceIdFormat() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => ['invalid_service_id'], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Invalid serviceId format. It should be an array of numeric values.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testInvalidTimestampFormat() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => ['1063423'], + 'serviceCount' => [0], + 'timestamp' => 'invalid_timestamp', + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Invalid timestamp format. It should be a positive numeric value.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + public function testEmptyServiceIdArray() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => [], + 'serviceCount' => [0], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Missing serviceId.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + + public function testInvalidServiceCount() + { + $this->setApiCalls([]); + + $parameters = [ + 'officeId' => '10546', + 'serviceId' => ['1063423'], + 'serviceCount' => ['invalid'], + 'timestamp' => "32526616522", + 'captchaSolution' => null + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'status' => 400, + 'errorMessage' => 'Invalid serviceCount format. It should be an array of non-negative numeric values.', + ] + ], + 'status' => 400 + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } + } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php index 2dd462f85..48279fd9f 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -5,7 +5,7 @@ class AppointmentUpdateTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AppointmentUpdate"; + protected $classname = "AppointmentUpdate"; public function testRendering() { $responseData = $this->renderJson(method: 'POST'); diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php index d40e1f28b..8d320b570 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php @@ -4,7 +4,7 @@ class AvailableAppointmentsListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AvailableAppointmentsList"; + protected $classname = "AvailableAppointmentsList"; public function testRendering() { @@ -17,28 +17,24 @@ public function testRendering() ] ] ); - + $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'officeId' => '10546', 'serviceId' => '1063423', 'serviceCount' => '1', ]; - + $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'appointmentTimestamps' => [32526616522], + 'status' => 200, + ]; $this->assertEquals(200, $response->getStatusCode()); - - $this->assertArrayHasKey('appointmentTimestamps', $responseBody); - $this->assertNotEmpty($responseBody['appointmentTimestamps']); - $this->assertTrue(is_array($responseBody['appointmentTimestamps'])); - $this->assertTrue(count($responseBody['appointmentTimestamps']) > 0); - $this->assertTrue(is_numeric($responseBody['appointmentTimestamps'][0])); - - $this->assertArrayHasKey('lastModified', $responseBody); - $this->assertTrue(is_numeric($responseBody['lastModified'])); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } + public function testEmptyAppointments() { @@ -53,25 +49,26 @@ public function testEmptyAppointments() ); $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'officeId' => '10546', 'serviceId' => '1063423', 'serviceCount' => '1', ]; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'appointmentTimestamps' => [], + 'errorCode' => "appointmentNotAvailable", + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; $this->assertEquals(404, $response->getStatusCode()); - - $this->assertArrayHasKey('errorCode', $responseBody); - $this->assertEquals('appointmentNotAvailable', $responseBody['errorCode']); - - $this->assertArrayHasKey('errorMessage', $responseBody); - $this->assertEquals('Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar', $responseBody['errorMessage']); - - $this->assertArrayHasKey('appointmentTimestamps', $responseBody); - $this->assertEmpty($responseBody['appointmentTimestamps']); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } public function testDateMissing() @@ -84,70 +81,86 @@ public function testDateMissing() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'date is required and must be a valid date', 'path' => 'date', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testOfficeIdMissing() { $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'serviceId' => '1063423', 'serviceCount' => '1', ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'officeId should be a 32-bit integer', 'path' => 'officeId', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testServiceIdMissing() { $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'officeId' => '10546', 'serviceCount' => '1', ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceId should be a comma-separated string of integers', 'path' => 'serviceId', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testServiceCountMissing() { $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'officeId' => '10546', 'serviceId' => '1063423', ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceCount should be a comma-separated string of integers', 'path' => 'serviceCount', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testDateAndOfficeIdMissing() @@ -159,17 +172,21 @@ public function testDateAndOfficeIdMissing() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'date is required and must be a valid date', 'path' => 'date', 'location' => 'body'], - $responseBody['errors'] - ); - $this->assertContains( - ['type' => 'field', 'msg' => 'officeId should be a 32-bit integer', 'path' => 'officeId', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testDateAndServiceIdMissing() @@ -181,17 +198,21 @@ public function testDateAndServiceIdMissing() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'date is required and must be a valid date', 'path' => 'date', 'location' => 'body'], - $responseBody['errors'] - ); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceId should be a comma-separated string of integers', 'path' => 'serviceId', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testDateAndServiceCountMissing() @@ -203,82 +224,98 @@ public function testDateAndServiceCountMissing() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'date is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'date is required and must be a valid date', 'path' => 'date', 'location' => 'body'], - $responseBody['errors'] - ); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceCount should be a comma-separated string of integers', 'path' => 'serviceCount', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testOfficeIdAndServiceIdMissing() { $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'serviceCount' => '1', ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'officeId should be a 32-bit integer', 'path' => 'officeId', 'location' => 'body'], - $responseBody['errors'] - ); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceId should be a comma-separated string of integers', 'path' => 'serviceId', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testOfficeIdAndServiceCountMissing() { $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'serviceId' => '1063423', ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'officeId should be a 32-bit integer', 'path' => 'officeId', 'location' => 'body'], - $responseBody['errors'] - ); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceCount should be a comma-separated string of integers', 'path' => 'serviceCount', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testServiceIdAndServiceCountMissing() { $parameters = [ - 'date' => '2024-09-21', + 'date' => '3000-09-21', 'officeId' => '10546', ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400 + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceId should be a comma-separated string of integers', 'path' => 'serviceId', 'location' => 'body'], - $responseBody['errors'] - ); - $this->assertContains( - ['type' => 'field', 'msg' => 'serviceCount should be a comma-separated string of integers', 'path' => 'serviceCount', 'location' => 'body'], - $responseBody['errors'] - ); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php index bd3113305..9591e64d1 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php @@ -4,7 +4,7 @@ class AvailableDaysListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\AvailableDaysList"; + protected $classname = "AvailableDaysList"; public function testRendering() { @@ -25,21 +25,18 @@ public function testRendering() 'serviceCount' => '1', ]; $response = $this->render([], $parameters, []); - - $this->assertStringContainsString( - '2024-08-21', - (string)$response->getBody() - ); - $this->assertStringContainsString( - '2024-08-22', - (string)$response->getBody() - ); - $this->assertStringContainsString( - '2024-08-23', - (string)$response->getBody() - ); - + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ + 'availableDays' => [ + "2024-08-21", "2024-08-22", "2024-08-23", "2024-08-26", "2024-08-27", "2024-08-28", "2024-08-29", "2024-08-30", + "2024-09-02", "2024-09-03", "2024-09-04", "2024-09-05", "2024-09-06", "2024-09-09", "2024-09-10", "2024-09-11", + "2024-09-12", "2024-09-13", "2024-09-16", "2024-09-17", "2024-09-18", "2024-09-19", "2024-09-20" + ], + 'status' => 200, + ]; $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } public function testNoAvailableDays() @@ -53,6 +50,7 @@ public function testNoAvailableDays() ] ] ); + $parameters = [ 'officeId' => '9999998', 'serviceId' => '1', @@ -60,16 +58,21 @@ public function testNoAvailableDays() 'startDate' => '2024-08-21', 'endDate' => '2024-08-23', ]; + $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'noAppointmentForThisDay', + 'errorMessage' => 'No available days found for the given criteria.', + 'status' => 404, + ] + ], + 'status' => 404, + ]; $this->assertEquals(404, $response->getStatusCode()); - $this->assertArrayHasKey('errorCode', $responseBody); - $this->assertEquals('noAppointmentForThisScope', $responseBody['errorCode']); - $this->assertArrayHasKey('errorMessage', $responseBody); - $this->assertEquals('No available days found for the given criteria', $responseBody['errorMessage']); - $this->assertArrayHasKey('availableDays', $responseBody); - $this->assertEmpty($responseBody['availableDays']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testInvalidDateFormat() @@ -83,13 +86,19 @@ public function testInvalidDateFormat() ]; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - - $this->assertEquals(500, $response->getStatusCode()); - $this->assertArrayHasKey('errorCode', $responseBody); - $this->assertArrayHasKey('errorMessage', $responseBody); - $this->assertEquals('An diesem Standort gibt es aktuell leider keine freien Termine', $responseBody['errorMessage']); - $this->assertArrayHasKey('availableDays', $responseBody); - $this->assertEmpty($responseBody['availableDays']); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'noAppointmentForThisOffice', + 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', + 'status' => 404, + ] + ], + 'status' => 404, + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + } public function testMissingStartDate() @@ -103,11 +112,17 @@ public function testMissingStartDate() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); } public function testMissingEndDate() @@ -121,12 +136,18 @@ public function testMissingEndDate() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - - $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - } + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingOfficeId() { @@ -139,12 +160,18 @@ public function testMissingOfficeId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][0]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingServiceId() { @@ -157,12 +184,18 @@ public function testMissingServiceId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('serviceId should be a 32-bit integer', $responseBody['errors'][0]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingServiceCount() { @@ -175,12 +208,18 @@ public function testMissingServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][0]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testEmptyServiceCount() { @@ -194,12 +233,18 @@ public function testEmptyServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][0]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testInvalidServiceCountFormat() { @@ -213,12 +258,18 @@ public function testInvalidServiceCountFormat() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][0]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testAllParametersMissing() { @@ -226,10 +277,33 @@ public function testAllParametersMissing() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(5, $responseBody['errors']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingStartDateAndEndDate() @@ -242,13 +316,22 @@ public function testMissingStartDateAndEndDate() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][1]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingOfficeIdAndServiceId() { @@ -260,13 +343,22 @@ public function testMissingOfficeIdAndServiceId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][0]['msg']); - $this->assertEquals('serviceId should be a 32-bit integer', $responseBody['errors'][1]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingServiceIdAndServiceCount() { @@ -278,13 +370,22 @@ public function testMissingServiceIdAndServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('serviceId should be a 32-bit integer', $responseBody['errors'][0]['msg']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][1]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingStartDateAndOfficeId() { @@ -296,12 +397,21 @@ public function testMissingStartDateAndOfficeId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][1]['msg']); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingEndDateAndServiceCount() @@ -314,13 +424,22 @@ public function testMissingEndDateAndServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][1]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingOfficeIdAndServiceCount() { @@ -332,14 +451,22 @@ public function testMissingOfficeIdAndServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][0]['msg']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][1]['msg']); - } - + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingStartDateEndDateAndOfficeId() { @@ -350,14 +477,26 @@ public function testMissingStartDateEndDateAndOfficeId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(3, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][1]['msg']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][2]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingStartDateEndDateAndServiceId() { @@ -368,14 +507,26 @@ public function testMissingStartDateEndDateAndServiceId() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(3, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][1]['msg']); - $this->assertEquals('serviceId should be a 32-bit integer', $responseBody['errors'][2]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingStartDateOfficeIdAndServiceCount() { @@ -386,14 +537,26 @@ public function testMissingStartDateOfficeIdAndServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(3, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][1]['msg']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][2]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testMissingEndDateOfficeIdAndServiceCount() { @@ -404,14 +567,26 @@ public function testMissingEndDateOfficeIdAndServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ], + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(3, $responseBody['errors']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('officeId should be a 32-bit integer', $responseBody['errors'][1]['msg']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][2]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } public function testEmptyStartDateAndEndDate() { @@ -425,14 +600,23 @@ public function testEmptyStartDateAndEndDate() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'startDate is required and must be a valid date.', + 'status' => 400, + ], + [ + 'errorMessage' => 'endDate is required and must be a valid date.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(2, $responseBody['errors']); - $this->assertEquals('startDate is required and must be a valid date', $responseBody['errors'][0]['msg']); - $this->assertEquals('endDate is required and must be a valid date', $responseBody['errors'][1]['msg']); - } - + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + public function testNonNumericServiceCount() { $parameters = [ @@ -445,11 +629,17 @@ public function testNonNumericServiceCount() $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - + $expectedResponse = [ + 'errors' => [ + [ + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', + 'status' => 400, + ] + ], + 'status' => 400, + ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertArrayHasKey('errors', $responseBody); - $this->assertCount(1, $responseBody['errors']); - $this->assertEquals('serviceCount should be a comma-separated string of integers', $responseBody['errors'][0]['msg']); - } + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index 010151f73..824f93cec 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -7,7 +7,7 @@ class CaptchaGetTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\CaptchaGet"; + protected $classname = "CaptchaGet"; public function setUp(): void { @@ -36,15 +36,15 @@ public function testCaptchaDetails() $parameters = []; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); - - $expectedDetails = [ + $expectedResponse = [ 'siteKey' => 'FAKE_SITE_KEY', 'captchaEndpoint' => 'https://api.friendlycaptcha.com/api/v1/siteverify', 'puzzle' => 'https://api.friendlycaptcha.com/api/v1/puzzle', - 'captchaEnabled' => true + 'captchaEnabled' => true, + 'status' => 200 ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals($expectedDetails, $responseBody); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php index 182e4b545..c61c1c5a9 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php @@ -5,7 +5,7 @@ class OfficesByServiceListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\OfficesByServiceList"; + protected $classname = "OfficesByServiceList"; public function testRendering() { @@ -30,6 +30,7 @@ public function testRendering() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -60,6 +61,7 @@ public function testRenderingRequestRelation() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -90,6 +92,7 @@ public function testRenderingMulti() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -109,26 +112,39 @@ public function testServiceNotFound() $response = $this->render([], [ 'serviceId' => '99999999' ], []); - + $expectedResponse = [ - 'status' => 404, - 'offices' => [], - 'error' => 'Office(s) not found for the provided serviceId(s)' + 'errors' => [ + [ + 'errorCode' => 'officesNotFound', + 'errorMessage' => 'Office(s) not found for the provided serviceId(s).', + 'status' => 404, + ] + ], + 'status' => 404 ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testNoServiceIdProvided() { $response = $this->render([], [], []); + $expectedResponse = [ - 'status' => 400, - 'offices' => [], - 'error' => 'Invalid serviceId(s)' + 'errors' => [ + [ + 'offices' => [], + 'errorMessage' => 'Invalid serviceId(s).', + 'status' => 400 + ] + ], + 'status' => 400 ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testPartialResultsWithWarning() @@ -155,8 +171,9 @@ public function testPartialResultsWithWarning() ], 'warning' => 'The following serviceId(s) were not found: 99999999' ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testPartialResultsWithWarningRequestRelation() @@ -187,8 +204,9 @@ public function testPartialResultsWithWarningRequestRelation() ], 'warning' => 'The following serviceId(s) were not found: 99999999' ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testDuplicateServiceIds() @@ -214,6 +232,7 @@ public function testDuplicateServiceIds() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -244,6 +263,8 @@ public function testDuplicateServiceIdsCombinable() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php index 06c9ee1de..d04746041 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesListTest.php @@ -5,9 +5,10 @@ class OfficesListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\OfficesList"; + protected $classname = "OfficesList"; - public function testRendering() { + public function testRendering() + { $this->setApiCalls([ [ 'function' => 'readGetResult', @@ -15,11 +16,13 @@ public function testRendering() { 'parameters' => [ 'resolveReferences' => 2, ], - 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + 'response' => $this->readFixture("GET_SourceGet_dldb.json") ] ]); - $responseData = $this->renderJson(); - $this->assertEqualsCanonicalizing([ + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ "offices" => [ [ "id" => "9999998", @@ -61,7 +64,11 @@ public function testRendering() { "displayInfo" => null ] ] - ] - ], $responseData); + ], + "status" => 200 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php index b312125cf..965dc9e33 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesServicesRelationsTest.php @@ -5,7 +5,7 @@ class OfficesServicesRelationsTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\OfficesServicesRelations"; + protected $classname = "OfficesServicesRelations"; public function testRendering() { @@ -16,10 +16,12 @@ public function testRendering() 'parameters' => [ 'resolveReferences' => 2, ], - 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + 'response' => $this->readFixture("GET_SourceGet_dldb.json") ] ]); - $responseData = $this->renderJson(); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); $expectedResponse = [ "offices" => [ [ @@ -95,8 +97,11 @@ public function testRendering() "serviceId" => "2", "slots" => 1 ] - ] + ], + "status" => 200 ]; - $this->assertEqualsCanonicalizing($expectedResponse, $responseData); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php index f3ab70d3a..4a3b95ca9 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -7,7 +7,7 @@ class ScopeByIdGetTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\ScopeByIdGet"; + protected $classname = "ScopeByIdGet"; public function testRendering() { @@ -39,9 +39,11 @@ public function testRendering() 'customTextfieldRequired' => '0', 'customTextfieldLabel' => 'Custom Label', 'captchaActivatedRequired' => '1', + 'displayInfo' => null ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -75,6 +77,7 @@ public function testRenderingMulti() 'customTextfieldRequired' => '0', 'customTextfieldLabel' => 'Custom Label', 'captchaActivatedRequired' => '1', + 'displayInfo' => null ], [ 'id' => '2', @@ -89,9 +92,11 @@ public function testRenderingMulti() 'customTextfieldRequired' => '1', 'customTextfieldLabel' => '', 'captchaActivatedRequired' => '0', + 'displayInfo' => null ], ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -111,29 +116,41 @@ public function testScopeNotFound() $response = $this->render([], [ 'scopeId' => '99' ], []); - + $expectedResponse = [ - 'status' => 404, - 'scopes' => [], - 'error' => 'Scope(s) not found' + 'errors' => [ + [ + 'errorCode' => "scopesNotFound", + 'errorMessage' => 'Scope(s) not found.', + 'status' => 404 + ] + ], + 'status' => 404 ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testNoScopeIdProvided() { $response = $this->render([], [], []); $expectedResponse = [ - 'status' => 400, - 'scopes' => [], - 'error' => 'Invalid scopeId(s)' + 'errors' => [ + [ + 'services' => [], + 'errorMessage' => 'Invalid scopeId(s).', + 'status' => 400 + ] + ], + 'status' => 400 ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } - public function testPartialResultsWithWarning() { $this->setApiCalls([ @@ -164,14 +181,16 @@ public function testPartialResultsWithWarning() 'customTextfieldRequired' => '0', 'customTextfieldLabel' => 'Custom Label', 'captchaActivatedRequired' => '1', + 'displayInfo' => null ] ], 'warning' => 'The following scopeId(s) were not found: 99' - ]; + ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); - $this->assertEquals(200, $response->getStatusCode()); + } - + public function testDuplicateScopeIds() { $this->setApiCalls([ @@ -204,12 +223,13 @@ public function testDuplicateScopeIds() 'customTextfieldRequired' => '0', 'customTextfieldLabel' => 'Custom Label', 'captchaActivatedRequired' => '1', + 'displayInfo' => null ] ] ]; - - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php index de3ee8fa0..d1b019f42 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopesListTest.php @@ -5,9 +5,10 @@ class ScopesListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\ScopesList"; + protected $classname = "ScopesList"; - public function testRendering() { + public function testRendering() + { $this->setApiCalls([ [ 'function' => 'readGetResult', @@ -15,10 +16,12 @@ public function testRendering() { 'parameters' => [ 'resolveReferences' => 2, ], - 'response' => $this->readFixture("GET_SourceGet_dldb.json"), // Use the same fixture as before + 'response' => $this->readFixture("GET_SourceGet_dldb.json") ] ]); - $responseData = $this->renderJson(); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); $expectedResponse = [ "scopes" => [ [ @@ -34,7 +37,8 @@ public function testRendering() { "customTextfieldActivated" => "1", "customTextfieldRequired" => "0", "customTextfieldLabel" => "Custom Label", - "captchaActivatedRequired" => "1" + "captchaActivatedRequired" => "1", + "displayInfo" => null ], [ "id" => "2", @@ -49,11 +53,14 @@ public function testRendering() { "customTextfieldActivated" => "0", "customTextfieldRequired" => "1", "customTextfieldLabel" => "", - "captchaActivatedRequired" => "0" + "captchaActivatedRequired" => "0", + "displayInfo" => null ] - ] + ], + "status" => 200, ]; - $this->assertEqualsCanonicalizing($expectedResponse, $responseData); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php index cce6908b5..54305a9a8 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php @@ -5,7 +5,7 @@ class ServicesByOfficeListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\ServicesByOfficeList"; + protected $classname = "ServicesByOfficeList"; public function testRendering() { @@ -31,6 +31,7 @@ public function testRendering() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -63,6 +64,7 @@ public function testRenderingMulti() ] ] ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } @@ -83,26 +85,40 @@ public function testServiceNotFound() $response = $this->render([], [ 'officeId' => '99999999' ], []); - + $expectedResponse = [ - 'status' => 404, - 'services' => [], - 'error' => 'Service(s) not found for the provided officeId(s)' + 'errors' => [ + [ + 'errorCode' => 'servicesNotFound', + 'errorMessage' => 'Service(s) not found for the provided officeId(s).', + 'status' => 404 + ] + ], + 'status' => 404 ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testNoOfficeIdProvided() { $response = $this->render([], [], []); $expectedResponse = [ - 'status' => 400, - 'services' => [], - 'error' => 'Invalid officeId(s)' + 'errors' => [ + [ + 'services' => [], + 'errorMessage' => 'Invalid officeId(s)', + 'status' => 400 + ] + ], + 'status' => 400 ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testPartialResultsWithWarning() @@ -129,9 +145,11 @@ public function testPartialResultsWithWarning() ] ], 'warning' => 'The following officeId(s) were not found: 99999999' - ]; - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + ]; + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } public function testDuplicateOfficeIds() @@ -158,6 +176,8 @@ public function testDuplicateOfficeIds() ] ] ]; + + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php index aa72571bb..dc4911ea4 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesListTest.php @@ -5,7 +5,7 @@ class ServicesListTest extends Base { - protected $classname = "\BO\Zmscitizenapi\Controllers\ServicesList"; + protected $classname = "ServicesList"; public function testRendering() { $this->setApiCalls([ @@ -15,10 +15,12 @@ public function testRendering() { 'parameters' => [ 'resolveReferences' => 2, ], - 'response' => $this->readFixture("GET_SourceGet_dldb.json"), + 'response' => $this->readFixture("GET_SourceGet_dldb.json") ] ]); - $responseData = $this->renderJson(); + + $response = $this->render(); + $responseBody = json_decode((string)$response->getBody(), true); $expectedResponse = [ "services" => [ [ @@ -31,8 +33,11 @@ public function testRendering() { "name" => "Unittest Source Dienstleistung 2", "maxQuantity" => 1 ] - ] + ], + "status" => 200, ]; - $this->assertEqualsCanonicalizing($expectedResponse, $responseData); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_SourceGet_dldb.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_SourceGet_dldb.json index dc19f7ce0..e49ecf422 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_SourceGet_dldb.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_SourceGet_dldb.json @@ -91,7 +91,8 @@ "customTextfieldActivated": "1", "customTextfieldRequired": "0", "customTextfieldLabel": "Custom Label", - "captchaActivatedRequired": "1" + "captchaActivatedRequired": "1", + "displayInfo": "Infos zum Standort." } } }, @@ -109,7 +110,8 @@ "customTextfieldActivated": "0", "customTextfieldRequired": "1", "customTextfieldLabel": "", - "captchaActivatedRequired": "0" + "captchaActivatedRequired": "0", + "displayInfo": "Infos zum Standort." } } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json index 5de67844b..322b9e35d 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json @@ -14,9 +14,9 @@ "customTextfield": "", "appointments": [ { - "date": "1726894800", + "date": "32526616522", "scope": { - "id": "39", + "id": "58", "source": "dldb" }, "availability": { @@ -76,7 +76,7 @@ }, "reminderTimestamp": 0, "scope": { - "id": "39", + "id": "58", "source": "dldb", "contact": { "name": "Gewerbeamt (KVR-III\/21)", diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json new file mode 100644 index 000000000..322b9e35d --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json @@ -0,0 +1,111 @@ +{ + "$schema": "https://localhost/terminvereinbarung/api/2/", + "meta": { + "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", + "error": false, + "exception": null, + "generated": "2024-08-29T13:55:41+02:00", + "server": "Zmsapi-ENV" + }, + "data": { + "0": { + "$schema": "https:\/\/schema.berlin.de\/queuemanagement\/process.json", + "amendment": "", + "customTextfield": "", + "appointments": [ + { + "date": "32526616522", + "scope": { + "id": "58", + "source": "dldb" + }, + "availability": { + "id": 0, + "weekday": { + "sunday": 0, + "monday": 0, + "tuesday": 0, + "wednesday": 0, + "thursday": 0, + "friday": 0, + "saturday": 0 + }, + "repeat": { + "afterWeeks": 1, + "weekOfMonth": 0 + }, + "bookable": { + "startInDays": 1, + "endInDays": 60 + }, + "workstationCount": { + "public": 0, + "callcenter": 0, + "intern": 0 + }, + "lastChange": 0, + "multipleSlotsAllowed": true, + "slotTimeInMinutes": 10, + "startDate": 0, + "endDate": 0, + "startTime": "0:00", + "endTime": "23:59", + "type": "appointment" + }, + "slotCount": "4" + } + ], + "apiclient": { + "shortname": "default" + }, + "authKey": "", + "createIP": "", + "createTimestamp": 1725635464, + "id": 0, + "archiveId": 0, + "queue": { + "$schema": "https:\/\/schema.berlin.de\/queuemanagement\/queue.json", + "arrivalTime": 0, + "callCount": 0, + "callTime": 0, + "number": 0, + "waitingTimeEstimate": 0, + "waitingTimeOptimistic": 0, + "waitingTime": 0, + "wayTime": 0 + }, + "reminderTimestamp": 0, + "scope": { + "id": "58", + "source": "dldb", + "contact": { + "name": "Gewerbeamt (KVR-III\/21)", + "street": "Implerstra\u00dfe 11", + "email": "", + "country": "Germany" + }, + "provider": { + "id": "10546", + "source": "dldb", + "contact": { + "city": "Muenchen", + "country": "Germany", + "name": "Gewerbeamt (KVR-III\/21)", + "postalCode": "81371", + "region": "Muenchen", + "street": "Implerstra\u00dfe", + "streetNumber": "11" + }, + "link": "https:\/\/service.berlin.de\/standort\/10546\/", + "name": "Gewerbeamt (KVR-III\/21)", + "displayName": "Gewerbeamt" + }, + "hint": "", + "lastChange": 1724883487, + "shortName": "" + }, + "status": "free", + "lastChange": 1725635464 + } + } +} \ No newline at end of file diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json new file mode 100644 index 000000000..9344088f9 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json @@ -0,0 +1,143 @@ +{ + "$schema": "https://localhost/terminvereinbarung/api/2/", + "meta": { + "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", + "error": false, + "generated": "2019-02-08T14:45:15+01:00", + "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + }, + "data": { + "$schema": "https://schema.berlin.de/queuemanagement/source.json", + "source": "unittest", + "contact": { + "name": "BerlinOnline Stadtportal GmbH", + "email": "test@example.com" + }, + "providers": [ + { + "id": "10546", + "source": "dldb", + "contact": { + "city": "Muenchen", + "country": "Germany", + "name": "Gewerbeamt (KVR-III\/21)", + "postalCode": "81371", + "region": "Muenchen", + "street": "Implerstra\u00dfe", + "streetNumber": "11" + }, + "link": "https:\/\/service.berlin.de\/standort\/10546\/", + "name": "Gewerbeamt (KVR-III\/21)", + "displayName": "Gewerbeamt" + } + ], + "requests": [ + { + "id": "1063423", + "link": "https:\/\/service.berlin.de\/dienstleistung\/1063423\/", + "name": "Gewerbe anmelden \/ Zweitschrift beantragen", + "group": "Sonstiges", + "source": "dldb" + } + ], + "scopes": [ + { + "id": "58", + "source": "dldb", + "contact": { + "name": "Gewerbeamt (KVR-III\/21)", + "street": "Implerstra\u00dfe 11", + "email": "", + "country": "Germany" + }, + "provider": { + "id": "10546", + "source": "dldb" + }, + "hint": "", + "lastChange": 1724192287, + "shortName": "Gewerbemeldungen", + "displayInfo": "Infos zum Standort.", + "preferences": { + "client": { + "telephoneActivated": "0", + "telephoneRequired": "1", + "customTextfieldActivated": "0", + "customTextfieldRequired": "1", + "customTextfieldLabel": "", + "captchaActivatedRequired": "0" + } + } + } + ], + "days": [ + { + "$schema": "https://schema.berlin.de/queuemanagement/day.json", + "year": "2024", + "month": "08", + "day": "21", + "status": "bookable", + "freeAppointments": { + "public": 10, + "intern": 5, + "callcenter": 0, + "type": "sum" + } + }, + { + "$schema": "https://schema.berlin.de/queuemanagement/day.json", + "year": "2024", + "month": "08", + "day": "22", + "status": "bookable", + "freeAppointments": { + "public": 15, + "intern": 10, + "callcenter": 0, + "type": "sum" + } + }, + { + "$schema": "https://schema.berlin.de/queuemanagement/day.json", + "year": "2024", + "month": "08", + "day": "23", + "status": "bookable", + "freeAppointments": { + "public": 12, + "intern": 7, + "callcenter": 0, + "type": "sum" + } + } + ], + "firstDay": { + "year": "2024", + "month": "08", + "day": "21" + }, + "lastDay": { + "year": "2024", + "month": "08", + "day": "23" + }, + "freeProcesses": [], + "label": "Unittest Source", + "editable": "1", + "lastChange": 1549546997, + "requestrelation": [ + { + "provider": { + "id": "10546", + "source": "unittest" + }, + "request": { + "id": "1063423", + "$ref": "/request/unittest/1/" + }, + "source": "unittest", + "slots": "2" + } + ] + } +} \ No newline at end of file diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json new file mode 100644 index 000000000..f3d8f1840 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://localhost/terminvereinbarung/api/2/", + "meta": { + "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", + "error": false, + "generated": "2024-09-24T16:48:37+02:00", + "server": "Zmsapi-ENV ()" + }, + "data": { + "$schema": "https://schema.berlin.de/queuemanagement/process.json", + "amendment": "", + "customTextfield": "", + "appointments": [ + { + "date": 32526616522, + "scope": { + "id": "58", + "source": "dldb" + }, + "availability": { + "id": 0, + "weekday": { + "sunday": 0, + "monday": 0, + "tuesday": 0, + "wednesday": 0, + "thursday": 0, + "friday": 0, + "saturday": 0 + }, + "repeat": { + "afterWeeks": 1, + "weekOfMonth": 0 + }, + "bookable": { + "startInDays": 1, + "endInDays": 60 + }, + "workstationCount": { + "public": 0, + "callcenter": 0, + "intern": 0 + }, + "lastChange": 0, + "multipleSlotsAllowed": true, + "slotTimeInMinutes": 10, + "startDate": 0, + "endDate": 0, + "startTime": "0:00", + "endTime": "23:59", + "type": "appointment" + }, + "slotCount": "4" + } + ], + "apiclient": { + "shortname": "default" + }, + "authKey": "b93e", + "displayInfo": "Infos zum Standort.", + "clients": [ + { + "familyName": "", + "email": "test@muenchen.de", + "emailSendCount": "0", + "notificationsSendCount": "0", + "surveyAccepted": "0", + "telephone": "" + } + ], + "createIP": "", + "createTimestamp": 1727189317, + "id": "101142", + "serviceCount": 1, + "archiveId": 0, + "queue": { + "$schema": "https:\/\/schema.berlin.de\/queuemanagement\/queue.json", + "arrivalTime": 1727691300, + "callCount": "0", + "callTime": 0, + "number": "101142", + "waitingTimeEstimate": 0, + "waitingTimeOptimistic": 0, + "waitingTime": null, + "wayTime": null, + "status": "reserved", + "lastCallTime": 0, + "destination": null, + "destinationHint": null, + "withAppointment": "1" + }, + "reminderTimestamp": "0", + "scope": { + "id": "58", + "source": "dldb" + }, + "status": "reserved", + "lastChange": 1727189317 + } +} \ No newline at end of file From 78b1e1edfcbda699e712857e6898fb65866a1b19 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Fri, 27 Sep 2024 18:03:41 +0200 Subject: [PATCH 10/42] feat(MPDZBS-877): Readd zmscitizenapi to github workflow --- .github/workflows/build-images.yaml | 2 ++ .github/workflows/unit-tests.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/build-images.yaml b/.github/workflows/build-images.yaml index 3b913d38e..1fed03e70 100644 --- a/.github/workflows/build-images.yaml +++ b/.github/workflows/build-images.yaml @@ -45,6 +45,8 @@ jobs: php_version: "8.0" - module: zmscalldisplay php_version: "8.0" + - module: zmscitizenapi + php_version: "8.0" - module: zmsmessaging php_version: "8.0" - module: zmsstatistic diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 57f376e2c..108cb342e 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -27,6 +27,8 @@ jobs: php_version: "8.0" - module: zmscalldisplay php_version: "8.0" + - module: zmscitizenapi + php_version: "8.0" - module: zmsdldb php_version: "8.0" - module: zmsentities From e1d16ad4f7720e6eea7005ff652372adac4ae35a Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Fri, 27 Sep 2024 18:11:04 +0200 Subject: [PATCH 11/42] feat(MPDZBS-877): Readd files lost in github history --- zmscitizenapi/.gitignore | 19 + zmscitizenapi/Makefile | 32 + zmscitizenapi/bin/build_swagger.js | 89 + zmscitizenapi/bin/configure | 28 + zmscitizenapi/composer.json | 57 + zmscitizenapi/composer.lock | 6781 +++++++++++++++++ zmscitizenapi/config.example.php | 17 + zmscitizenapi/log/.gitignore | 2 + zmscitizenapi/package.json | 26 + zmscitizenapi/phpunit.xml | 27 + zmscitizenapi/public/doc/assets/.gitkeep | 0 zmscitizenapi/public/doc/index.html | 23 + zmscitizenapi/public/doc/logo.png | Bin 0 -> 26155 bytes zmscitizenapi/public/doc/partials/basic.yaml | 7 + .../public/doc/partials/definitions.yaml | 71 + zmscitizenapi/public/doc/partials/info.yaml | 27 + zmscitizenapi/public/doc/partials/tags.yaml | 6 + .../public/doc/partials/version.yaml | 1 + zmscitizenapi/public/doc/schema | 1 + zmscitizenapi/public/index.php | 4 + zmscitizenapi/routing.php | 602 ++ .../src/Zmscitizenapi/Helper/ErrorHandler.php | 16 + .../Middleware/MaintenanceMiddleware.php | 18 + zmscitizenapi/templates/.keep | 0 .../Zmscitizenapi/AppointmentCancelTest.php | 14 + .../Zmscitizenapi/AppointmentConfirmTest.php | 13 + .../AppointmentPreconfirmTest.php | 14 + .../Zmscitizenapi/AppointmentUpdateTest.php | 14 + zmscitizenapi/tests/Zmscitizenapi/Base.php | 29 + .../tests/Zmscitizenapi/bootstrap.php | 11 + .../fixtures/GET_appointments_empty.json | 148 + .../Zmscitizenapi/fixtures/GET_calendar.json | 506 ++ .../fixtures/GET_calendar_empty_days.json | 91 + .../Zmscitizenapi/fixtures/GET_process.json | 196 + 34 files changed, 8890 insertions(+) create mode 100644 zmscitizenapi/.gitignore create mode 100644 zmscitizenapi/Makefile create mode 100755 zmscitizenapi/bin/build_swagger.js create mode 100755 zmscitizenapi/bin/configure create mode 100644 zmscitizenapi/composer.json create mode 100644 zmscitizenapi/composer.lock create mode 100644 zmscitizenapi/config.example.php create mode 100644 zmscitizenapi/log/.gitignore create mode 100644 zmscitizenapi/package.json create mode 100644 zmscitizenapi/phpunit.xml create mode 100644 zmscitizenapi/public/doc/assets/.gitkeep create mode 100644 zmscitizenapi/public/doc/index.html create mode 100644 zmscitizenapi/public/doc/logo.png create mode 100644 zmscitizenapi/public/doc/partials/basic.yaml create mode 100644 zmscitizenapi/public/doc/partials/definitions.yaml create mode 100644 zmscitizenapi/public/doc/partials/info.yaml create mode 100644 zmscitizenapi/public/doc/partials/tags.yaml create mode 100644 zmscitizenapi/public/doc/partials/version.yaml create mode 120000 zmscitizenapi/public/doc/schema create mode 100644 zmscitizenapi/public/index.php create mode 100644 zmscitizenapi/routing.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php create mode 100644 zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php create mode 100644 zmscitizenapi/templates/.keep create mode 100644 zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/Base.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/bootstrap.php create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_empty.json create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar.json create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar_empty_days.json create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_process.json diff --git a/zmscitizenapi/.gitignore b/zmscitizenapi/.gitignore new file mode 100644 index 000000000..460d49306 --- /dev/null +++ b/zmscitizenapi/.gitignore @@ -0,0 +1,19 @@ +node_modules +vendor +vendor.zmsbase +config.php +tags +.DS_Store +public/_tests +public/doc/assets/redoc.min.js +public/doc/swagger.* +public/_test/assets/swagger-ui-bundle.js +public/_test/assets/swagger-ui.css +public/_test/assets/swagger-ui-standalone-preset.js +cache +vendor.old +vendor.update +#composer.lock +VERSION +package-lock.json +.phpunit.result.cache \ No newline at end of file diff --git a/zmscitizenapi/Makefile b/zmscitizenapi/Makefile new file mode 100644 index 000000000..7339248b1 --- /dev/null +++ b/zmscitizenapi/Makefile @@ -0,0 +1,32 @@ +.PHONY: help now dev live watch + +help: # This help + @echo "Possible Targets:" + @grep -P "^\w+:" Makefile|sort|perl -pe 's/^(\w+):([^\#]+)(\#\s*(.*))?/ \1\n\t\4\n/' + +now: # Dummy target + +dev: # init development system + COMPOSER=composer.json composer update + npm install + +live: # init live system, delete unnecessary libs + composer install --no-dev --prefer-dist + bin/configure + npm install + npm run build + npm run doc + npx swagger-cli bundle -o public/doc/swagger.json public/doc/swagger.yaml + +fix: # run code fixing + php vendor/bin/phpcbf --standard=psr2 src/ + php vendor/bin/phpcbf --standard=psr2 tests/ + +openapi: # Swagger docs on changes + ./bin/doc + +coverage: + php vendor/bin/phpunit --coverage-html public/_tests/coverage/ + +paratest: # init parallel unit testing with 5 processes + vendor/bin/paratest -p20 --coverage-html public/_tests/coverage/ diff --git a/zmscitizenapi/bin/build_swagger.js b/zmscitizenapi/bin/build_swagger.js new file mode 100755 index 000000000..4529bb3d3 --- /dev/null +++ b/zmscitizenapi/bin/build_swagger.js @@ -0,0 +1,89 @@ +var fs = require('fs'); +const swaggerParser = require('swagger-parser'); +const swaggerJsdoc = require('swagger-jsdoc'); +const yaml = require('js-yaml'); + +const options = { + definition: { + openapi: '2.0.0', + info: { + version: readApiVersion(), + title: "ZMS API" + }, + }, + apis: ['./routing.php'] + }; + +const openapiSpecification = swaggerJsdoc(options); + +buildSwagger(); +validateSwagger(); + +function validateSwagger() { + + fs.stat('public/doc/swagger.yaml', function(error, stats) { + var routessize = stats.size; + if (error) { + console.log(error); + } else { + console.log("Found public/doc/swagger.yaml with " + routessize + " bytes"); + } + + swaggerParser.validate('public/doc/swagger.yaml', (err, api) => { + if (err) { + console.error(err); + } + else { + console.log("Validated API %s, Version: %s", api.info.title, api.info.version); + } + }) + }); +} + +function buildSwagger() { + let version = readFileContent('public/doc/partials/version.yaml') + "\n"; + let info = readFileContent('public/doc/partials/info.yaml'); + //append current api version to info + info = info + "\n version: '" + readFileContent("./VERSION").trim() + "'\n"; + + let basics = readFileContent('public/doc/partials/basic.yaml') + "\n"; + let paths = { + paths: + openapiSpecification.paths, + } + let tags = readFileContent('public/doc/partials/tags.yaml'); + let definitions = readFileContent('public/doc/partials/definitions.yaml'); + writeSwaggerFile(version + info + basics + tags + yaml.dump(paths) + definitions) + +} + +function writeSwaggerFile(data) +{ + try { + fs.writeFileSync('public/doc/swagger.yaml', data, 'utf8'); + console.log("Build new swagger file successfully!"); + } catch (e) { + console.log(e); + } +} + +function readFileContent(file) { + try { + const data = fs.readFileSync(file, 'utf8'); + return data; + } catch (e) { + console.log(e); + } +} + +function readApiVersion() { + fs.readFile('./VERSION', 'utf8' , (err, data) => { + if (err) { + console.error(err) + return + } + return data; + }) +} + + diff --git a/zmscitizenapi/bin/configure b/zmscitizenapi/bin/configure new file mode 100755 index 000000000..30144d9d3 --- /dev/null +++ b/zmscitizenapi/bin/configure @@ -0,0 +1,28 @@ +#!/bin/bash + +REALPATH=$(which realpath || which readlink) +REALPATH=$([[ "$REALPATH" =~ 'readlink' ]] && echo "$REALPATH -e" || echo "$REALPATH") +DIR=$(dirname $($REALPATH ${BASH_SOURCE[0]})) +ROOT=$($REALPATH $DIR/..) + +#echo "Get DLDB Exports" +#$ROOT/bin/getDldbExport + +echo -n "Configuring application" +mkdir -p $ROOT/.git/hooks +ln -sf $ROOT/bin/test $ROOT/.git/hooks/pre-commit +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-checkout +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-commit +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-merge + +if [ ! -e $ROOT/config.php ] +then + cp $ROOT/config.example.php $ROOT/config.php +fi +test -d $ROOT/cache && chmod -fR a+rwX $ROOT/cache || echo "Could not chmod cache files" + + +#VERSION=`git symbolic-ref -q --short HEAD || git describe --tags --exact-match` +VERSION=`git describe --tags --always` +echo $VERSION > $ROOT/VERSION +echo " $VERSION" diff --git a/zmscitizenapi/composer.json b/zmscitizenapi/composer.json new file mode 100644 index 000000000..422f7a875 --- /dev/null +++ b/zmscitizenapi/composer.json @@ -0,0 +1,57 @@ +{ + "name": "eappointment/zmscitizenapi", + "description": "This application offers a REST-like interface for citizens on the internet.", + "license": "EUPL-1.2", + "authors": [], + "repositories": [ + { + "type": "path", + "url": "../*", + "options": { + "symlink": true + } + } + ], + "config": { + "platform": { + "php": "8.0" + }, + "allow-plugins": { + "php-http/discovery": true + } + }, + "require-dev": { + "phpmd/phpmd": "@stable", + "squizlabs/php_codesniffer": "*", + "phpunit/phpunit": "^9.5.4", + "helmich/phpunit-psr7-assert": "^4.3.0", + "phpspec/prophecy-phpunit": "^2.0.0" + }, + "require": { + "eappointment/mellon": "@dev", + "eappointment/zmsslim": "@dev", + "eappointment/zmsclient": "@dev", + "eappointment/zmsentities": "@dev" + }, + "scripts": { + "clean": "rm -f public/doc/assets/*.* && rm -f public/_test/assets/*.*", + "command": "bin/configure", + "wget-files": [ + "wget https://eappointment.gitlab.io/zmsapi/doc/swagger.json -O public/doc/swagger.json", + "wget https://eappointment.gitlab.io/zmsapi/doc/assets/redoc.min.js -O public/doc/assets/redoc.min.js" + ], + "post-install-cmd": [ + "@wget-files" + ], + "post-update-cmd": [ + "@wget-files" + ] + }, + "bin": [], + "autoload": { + "psr-4": { + "BO\\Zmscitizenapi\\": "src/Zmscitizenapi/", + "BO\\Zmscitizenapi\\Tests\\": "tests/Zmscitizenapi/" + } + } +} diff --git a/zmscitizenapi/composer.lock b/zmscitizenapi/composer.lock new file mode 100644 index 000000000..76f43aad0 --- /dev/null +++ b/zmscitizenapi/composer.lock @@ -0,0 +1,6781 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "323ae4e4d3276cd30903570c163d1e9e", + "packages": [ + { + "name": "aronduby/dump", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/aronduby/dump.git", + "reference": "8b3eb9b54248d520e7ad2f9f1295e2862056fb28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aronduby/dump/zipball/8b3eb9b54248d520e7ad2f9f1295e2862056fb28", + "reference": "8b3eb9b54248d520e7ad2f9f1295e2862056fb28", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "": "src/", + "D\\": "src/D/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "authors": [ + { + "name": "Aron Duby", + "email": "aron.duby@gmail.com" + } + ], + "description": "D::ump - a PHP 5.4 print_r/var_dump replacement base on Krumo", + "keywords": [ + "debug", + "debugging", + "dump", + "krumo", + "pretty", + "print", + "print_r", + "var_dump" + ], + "support": { + "issues": "https://github.com/aronduby/dump/issues", + "source": "https://github.com/aronduby/dump/tree/0.9.1" + }, + "time": "2016-09-30T05:02:34+00:00" + }, + { + "name": "clue/stream-filter", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/clue/stream-filter.git", + "reference": "049509fef80032cb3f051595029ab75b49a3c2f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/stream-filter/zipball/049509fef80032cb3f051595029ab75b49a3c2f7", + "reference": "049509fef80032cb3f051595029ab75b49a3c2f7", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "Clue\\StreamFilter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "A simple and modern approach to stream filtering in PHP", + "homepage": "https://github.com/clue/stream-filter", + "keywords": [ + "bucket brigade", + "callback", + "filter", + "php_user_filter", + "stream", + "stream_filter_append", + "stream_filter_register" + ], + "support": { + "issues": "https://github.com/clue/stream-filter/issues", + "source": "https://github.com/clue/stream-filter/tree/v1.7.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2023-12-20T15:40:13+00:00" + }, + { + "name": "eappointment/mellon", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../mellon", + "reference": "0d261e9c33e48b356b4130cce2beb95c95b9d28a" + }, + "require": { + "ext-json": ">=0", + "ext-pcre": ">=0", + "php": ">=7.0.0" + }, + "require-dev": { + "phpmd/phpmd": "^2.8.0", + "phpunit/phpunit": "^9.5.4", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Mellon\\": "src/Mellon/" + } + }, + "scripts": { + "test": [ + "php -dzend_extension=xdebug.so vendor/bin/phpunit --coverage-html coverage/" + ], + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Validator for parameters and validation helper", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "eappointment/zmsclient", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../zmsclient", + "reference": "249f1aa96e13e3c2379353bf33539f0174fced53" + }, + "require": { + "aronduby/dump": "^0.9", + "eappointment/mellon": "@dev", + "eappointment/zmsentities": "@dev", + "eappointment/zmsslim": "@dev", + "ext-curl": ">=0", + "ext-json": ">=1.0", + "ext-mbstring": ">=0", + "ext-pcre": ">=0", + "php": ">=7.3.0", + "php-http/curl-client": "^2.2", + "psr/http-message": "^1.0", + "slim/psr7": "^1.5", + "tracy/tracy": "^2.9", + "twig/twig": "3.*" + }, + "require-dev": { + "helmich/phpunit-psr7-assert": "^4.3.0", + "phpmd/phpmd": "@stable", + "phpspec/prophecy-phpunit": "^2.0.0", + "phpunit/phpunit": "^9.5.4", + "squizlabs/php_codesniffer": "^3.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Zmsclient\\": "src/Zmsclient/", + "BO\\Zmsclient\\Tests\\": "tests/Zmsclient/" + } + }, + "scripts": { + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Use this library to fetch data from the eappointment API via HTTP.", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "eappointment/zmsentities", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../zmsentities", + "reference": "0d261e9c33e48b356b4130cce2beb95c95b9d28a" + }, + "require": { + "eappointment/mellon": "@dev", + "ext-curl": ">=0", + "ext-json": ">=1.0", + "ext-mbstring": ">=0", + "ext-pcre": ">=0", + "giggsey/libphonenumber-for-php": "^8.8.4", + "league/html-to-markdown": "^5.0", + "league/json-guard": "^1.0", + "php": ">=7.3.0", + "symfony/translation": "^5.4", + "symfony/twig-bridge": "^5.4", + "twig/intl-extra": "^3.4", + "twig/twig": "3.*" + }, + "require-dev": { + "helmich/phpunit-psr7-assert": "^4.3.0", + "league/json-reference": "^1.0", + "phpmd/phpmd": "@stable", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.5.8", + "yuloh/json-guard-cli": "^0.3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Zmsentities\\": "src/Zmsentities/", + "BO\\Zmsentities\\Tests\\": "tests/Zmsentities/" + } + }, + "scripts": { + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Entity definitions for eappoinment", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "eappointment/zmsslim", + "version": "dev-feature-MPDZBS-877-zmscitizenapi", + "dist": { + "type": "path", + "url": "../zmsslim", + "reference": "0d261e9c33e48b356b4130cce2beb95c95b9d28a" + }, + "require": { + "eappointment/mellon": "@dev", + "ext-json": "*", + "ext-posix": "*", + "monolog/monolog": "1.*", + "php": ">=7.3.0", + "slim/http-cache": "1.*", + "slim/psr7": "^1.5", + "slim/slim": "4.*", + "slim/twig-view": "3.*", + "stevenmaguire/oauth2-keycloak": "^4.0", + "symfony/translation": "^5.2", + "symfony/twig-bridge": "^5.2", + "tracy/tracy": "^2.9", + "twig/twig": "3.*" + }, + "provide": { + "psr/container-implementation": "2.0" + }, + "require-dev": { + "helmich/phpunit-psr7-assert": "^4.3.0", + "phpmd/phpmd": "@stable", + "phpunit/phpunit": "^9.5.4", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "BO\\Slim\\": "src/Slim/", + "BO\\Slim\\Tests\\": "tests/Slim/" + } + }, + "scripts": { + "command": [ + "bin/configure" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mathias Fischer", + "email": "mathias.fischer@berlinonline.de" + } + ], + "description": "Basic configuration for a slim framework", + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", + "shasum": "" + }, + "require": { + "php": "^7.4||^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.5||^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^1.0||^2.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" + }, + "time": "2023-12-01T16:26:39+00:00" + }, + { + "name": "giggsey/libphonenumber-for-php", + "version": "8.13.36", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php.git", + "reference": "9ca4179e4332d21578cb29f0c0406f0a2b8803e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/9ca4179e4332d21578cb29f0c0406f0a2b8803e3", + "reference": "9ca4179e4332d21578cb29f0c0406f0a2b8803e3", + "shasum": "" + }, + "require": { + "giggsey/locale": "^1.7|^2.0", + "php": ">=5.3.2", + "symfony/polyfill-mbstring": "^1.17" + }, + "replace": { + "giggsey/libphonenumber-for-php-lite": "self.version" + }, + "require-dev": { + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^1.0|^2.0", + "symfony/console": "^2.8|^3.0|^v4.4|^v5.2", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php" + }, + "time": "2024-05-03T06:27:03+00:00" + }, + { + "name": "giggsey/locale", + "version": "2.6", + "source": { + "type": "git", + "url": "https://github.com/giggsey/Locale.git", + "reference": "37874fa473131247c348059fb7b8985efc18b5ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/Locale/zipball/37874fa473131247c348059fb7b8985efc18b5ea", + "reference": "37874fa473131247c348059fb7b8985efc18b5ea", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "ext-json": "*", + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.5|^9.5", + "symfony/console": "^5.0|^6.0", + "symfony/filesystem": "^5.0|^6.0", + "symfony/finder": "^5.0|^6.0", + "symfony/process": "^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Giggsey\\Locale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "Locale functions required by libphonenumber-for-php", + "support": { + "issues": "https://github.com/giggsey/Locale/issues", + "source": "https://github.com/giggsey/Locale/tree/2.6" + }, + "time": "2024-04-18T19:31:19+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:35:24+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:19:20+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-12-03T20:05:35+00:00" + }, + { + "name": "league/html-to-markdown", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "^1.1.0", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^8.5 || ^9.2", + "scrutinizer/ocular": "^1.6", + "unleashedtech/php-coding-standard": "^2.7 || ^3.0", + "vimeo/psalm": "^4.22 || ^5.0" + }, + "bin": [ + "bin/html-to-markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "description": "An HTML-to-markdown conversion helper for PHP", + "homepage": "https://github.com/thephpleague/html-to-markdown", + "keywords": [ + "html", + "markdown" + ], + "support": { + "issues": "https://github.com/thephpleague/html-to-markdown/issues", + "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown", + "type": "tidelift" + } + ], + "time": "2023-07-12T21:21:09+00:00" + }, + { + "name": "league/json-guard", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/json-guard.git", + "reference": "596059d2c013bcea1a8a1386bd0e60d32ef39eb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/json-guard/zipball/596059d2c013bcea1a8a1386bd0e60d32ef39eb9", + "reference": "596059d2c013bcea1a8a1386bd0e60d32ef39eb9", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": ">=5.6.0", + "psr/container": "^1.0" + }, + "require-dev": { + "ext-curl": "*", + "json-schema/json-schema-test-suite": "1.2.0", + "league/json-reference": "1.0.0", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "~2.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "League\\JsonGuard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Allan", + "email": "matthew.james.allan@gmail.com", + "homepage": "https://mattallan.org", + "role": "Developer" + } + ], + "description": "A validator for JSON using json-schema.", + "homepage": "https://github.com/thephpleague/json-guard", + "keywords": [ + "json", + "json-schema", + "json-schema.org", + "schema", + "validation" + ], + "support": { + "issues": "https://github.com/thephpleague/json-guard/issues", + "source": "https://github.com/thephpleague/json-guard/tree/master" + }, + "abandoned": true, + "time": "2017-05-03T21:12:30+00:00" + }, + { + "name": "league/oauth2-client", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-client.git", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8", + "reference": "160d6274b03562ebeb55ed18399281d8118b76c8", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "paragonie/random_compat": "^1 || ^2 || ^9.99", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.5", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpunit/phpunit": "^5.7 || ^6.0 || ^9.5", + "squizlabs/php_codesniffer": "^2.3 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + }, + { + "name": "Woody Gilk", + "homepage": "https://github.com/shadowhand", + "role": "Contributor" + } + ], + "description": "OAuth 2.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "identity", + "idp", + "oauth", + "oauth2", + "single sign on" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth2-client/issues", + "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0" + }, + "time": "2023-04-16T18:19:15+00:00" + }, + { + "name": "monolog/monolog", + "version": "1.27.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "904713c5929655dc9b97288b69cfeedad610c9a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/904713c5929655dc9b97288b69cfeedad610c9a1", + "reference": "904713c5929655dc9b97288b69cfeedad610c9a1", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpstan/phpstan": "^0.12.59", + "phpunit/phpunit": "~4.5", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/1.27.1" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2022-06-09T08:53:42+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "php-http/curl-client", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/curl-client.git", + "reference": "0b869922458b1cde9137374545ed4fff7ac83623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/curl-client/zipball/0b869922458b1cde9137374545ed4fff7ac83623", + "reference": "0b869922458b1cde9137374545ed4fff7ac83623", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": "^7.4 || ^8.0", + "php-http/discovery": "^1.6", + "php-http/httplug": "^2.0", + "php-http/message": "^1.2", + "psr/http-client": "^1.0", + "psr/http-factory-implementation": "^1.0", + "symfony/options-resolver": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0", + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.0", + "laminas/laminas-diactoros": "^2.0", + "php-http/client-integration-tests": "^3.0", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^7.5 || ^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\Curl\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Михаил Красильников", + "email": "m.krasilnikov@yandex.ru" + } + ], + "description": "PSR-18 and HTTPlug Async client with cURL", + "homepage": "http://php-http.org", + "keywords": [ + "curl", + "http", + "psr-18" + ], + "support": { + "issues": "https://github.com/php-http/curl-client/issues", + "source": "https://github.com/php-http/curl-client/tree/2.3.2" + }, + "time": "2024-03-03T08:21:07+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.19.4", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "0700efda8d7526335132360167315fdab3aeb599" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599", + "reference": "0700efda8d7526335132360167315fdab3aeb599", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.19.4" + }, + "time": "2024-03-29T13:00:05+00:00" + }, + { + "name": "php-http/httplug", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.4.0" + }, + "time": "2023-04-14T15:10:03+00:00" + }, + { + "name": "php-http/message", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/message.git", + "reference": "5997f3289332c699fa2545c427826272498a2088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message/zipball/5997f3289332c699fa2545c427826272498a2088", + "reference": "5997f3289332c699fa2545c427826272498a2088", + "shasum": "" + }, + "require": { + "clue/stream-filter": "^1.5", + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.6", + "ext-zlib": "*", + "guzzlehttp/psr7": "^1.0 || ^2.0", + "laminas/laminas-diactoros": "^2.0 || ^3.0", + "php-http/message-factory": "^1.0.2", + "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", + "slim/slim": "^3.0" + }, + "suggest": { + "ext-zlib": "Used with compressor/decompressor streams", + "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", + "laminas/laminas-diactoros": "Used with Diactoros Factories", + "slim/slim": "Used with Slim Framework PSR-7 implementation" + }, + "type": "library", + "autoload": { + "files": [ + "src/filters.php" + ], + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTP Message related tools", + "homepage": "http://php-http.org", + "keywords": [ + "http", + "message", + "psr-7" + ], + "support": { + "issues": "https://github.com/php-http/message/issues", + "source": "https://github.com/php-http/message/tree/1.16.1" + }, + "time": "2024-03-07T13:22:09+00:00" + }, + { + "name": "php-http/promise", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.3.1" + }, + "time": "2024-03-15T13:55:21+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "slim/http-cache", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-HttpCache.git", + "reference": "d1a091aca45695a2159194132872f4a544416bc9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-HttpCache/zipball/d1a091aca45695a2159194132872f4a544416bc9", + "reference": "d1a091aca45695a2159194132872f4a544416bc9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.28", + "phpunit/phpunit": "^8.5.13 || ^9.3.8", + "slim/psr7": "^1.1", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\HttpCache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + } + ], + "description": "Slim Framework HTTP cache middleware and service provider", + "homepage": "https://www.slimframework.com", + "keywords": [ + "cache", + "framework", + "middleware", + "slim" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-HttpCache/issues", + "source": "https://github.com/slimphp/Slim-HttpCache/tree/1.1.0" + }, + "time": "2020-12-08T17:32:05+00:00" + }, + { + "name": "slim/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-Psr7.git", + "reference": "72d2b2bac94ab4575d369f605dbfafbe168d3163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/72d2b2bac94ab4575d369f605dbfafbe168d3163", + "reference": "72d2b2bac94ab4575d369f605dbfafbe168d3163", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "php": "^7.4 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0", + "symfony/polyfill-php80": "^1.26" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.3", + "ext-json": "*", + "http-interop/http-factory-tests": "^0.9.0", + "php-http/psr7-integration-tests": "1.1", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Psr7\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Strict PSR-7 implementation", + "homepage": "https://www.slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Psr7/issues", + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.6.1" + }, + "time": "2023-04-17T16:02:20+00:00" + }, + { + "name": "slim/slim", + "version": "4.13.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim.git", + "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/038fd5713d5a41636fdff0e8dcceedecdd17fc17", + "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6", + "slim/http": "^1.3", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\": "Slim" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" + } + ], + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "https://www.slimframework.com", + "keywords": [ + "api", + "framework", + "micro", + "router" + ], + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } + ], + "time": "2024-03-03T21:25:30+00:00" + }, + { + "name": "slim/twig-view", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Twig-View.git", + "reference": "1b351536b9a07ed90a3563ee9d71a987c5d74610" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/1b351536b9a07ed90a3563ee9d71a987c5d74610", + "reference": "1b351536b9a07ed90a3563ee9d71a987c5d74610", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/http-message": "^1.1 || ^2.0", + "slim/slim": "^4.12", + "symfony/polyfill-php81": "^1.29", + "twig/twig": "^3.8" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.10.59", + "phpunit/phpunit": "^9.6", + "psr/http-factory": "^1.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Views\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Slim Framework 4 view helper built on top of the Twig 3 templating component", + "homepage": "https://www.slimframework.com", + "keywords": [ + "framework", + "slim", + "template", + "twig", + "view" + ], + "support": { + "issues": "https://github.com/slimphp/Twig-View/issues", + "source": "https://github.com/slimphp/Twig-View/tree/3.4.0" + }, + "time": "2024-04-28T20:36:39+00:00" + }, + { + "name": "stevenmaguire/oauth2-keycloak", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/stevenmaguire/oauth2-keycloak.git", + "reference": "05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stevenmaguire/oauth2-keycloak/zipball/05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d", + "reference": "05ead6bb6bcd2b6f96dfae87c769dcd3e5f6129d", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^4.0 || ^5.0 || ^6.0", + "league/oauth2-client": "^2.0", + "php": "~7.2 || ~8.0" + }, + "require-dev": { + "mockery/mockery": "~1.5.0", + "phpunit/phpunit": "~9.6.4", + "squizlabs/php_codesniffer": "~3.7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Stevenmaguire\\OAuth2\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steven Maguire", + "email": "stevenmaguire@gmail.com", + "homepage": "https://github.com/stevenmaguire" + } + ], + "description": "Keycloak OAuth 2.0 Client Provider for The PHP League OAuth2-Client", + "keywords": [ + "authorisation", + "authorization", + "client", + "keycloak", + "oauth", + "oauth2" + ], + "support": { + "issues": "https://github.com/stevenmaguire/oauth2-keycloak/issues", + "source": "https://github.com/stevenmaguire/oauth2-keycloak/tree/4.0.0" + }, + "time": "2023-03-14T09:43:47+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-24T14:02:46+00:00" + }, + { + "name": "symfony/intl", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "0ae24e7ead0761e3e29e89c3336353b991c90f96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/0ae24e7ead0761e3e29e89c3336353b991c90f96", + "reference": "0ae24e7ead0761e3e29e89c3336353b991c90f96", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "classmap": [ + "Resources/stubs" + ], + "exclude-from-classmap": [ + "/Tests/", + "/Resources/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a PHP replacement layer for the C intl extension that includes additional data from the ICU library", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "support": { + "source": "https://github.com/symfony/intl/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "1303bb73d6c3882f07c618129295503085dfddb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1303bb73d6c3882f07c618129295503085dfddb9", + "reference": "1303bb73d6c3882f07c618129295503085dfddb9", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "21bd091060673a1177ae842c0ef8fe30893114d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2", + "reference": "21bd091060673a1177ae842c0ef8fe30893114d2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/translation", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "0fabede35e3985c4f96089edeeefe8313e15ca3a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/0fabede35e3985c4f96089edeeefe8313e15ca3a", + "reference": "0fabede35e3985c4f96089edeeefe8313e15ca3a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^2.3" + }, + "conflict": { + "symfony/config": "<4.4", + "symfony/console": "<5.3", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" + }, + "provide": { + "symfony/translation-implementation": "2.3" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b0073a77ac0b7ea55131020e87b1e3af540f4664", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T13:51:25+00:00" + }, + { + "name": "symfony/twig-bridge", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "1384448132165f95f76ca67cd722c560e29b8245" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/1384448132165f95f76ca67cd722c560e29b8245", + "reference": "1384448132165f95f76ca67cd722c560e29b8245", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<5.3", + "symfony/form": "<5.4.21|>=6,<6.2.7", + "symfony/http-foundation": "<5.3", + "symfony/http-kernel": "<4.4", + "symfony/translation": "<5.2", + "symfony/workflow": "<5.2" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^4.4|^5.0|^6.0", + "symfony/console": "^5.3|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/form": "^5.4.21|^6.2.7", + "symfony/http-foundation": "^5.3|^6.0", + "symfony/http-kernel": "^4.4|^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/mime": "^5.2|^6.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/routing": "^4.4|^5.0|^6.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^4.4|^5.0|^6.0", + "symfony/security-csrf": "^4.4|^5.0|^6.0", + "symfony/security-http": "^4.4|^5.0|^6.0", + "symfony/serializer": "^5.4.35|~6.3.12|^6.4.3", + "symfony/stopwatch": "^4.4|^5.0|^6.0", + "symfony/translation": "^5.2|^6.0", + "symfony/web-link": "^4.4|^5.0|^6.0", + "symfony/workflow": "^5.2|^6.0", + "symfony/yaml": "^4.4|^5.0|^6.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" + }, + "suggest": { + "symfony/asset": "For using the AssetExtension", + "symfony/expression-language": "For using the ExpressionExtension", + "symfony/finder": "", + "symfony/form": "For using the FormExtension", + "symfony/http-kernel": "For using the HttpKernelExtension", + "symfony/routing": "For using the RoutingExtension", + "symfony/security-core": "For using the SecurityExtension", + "symfony/security-csrf": "For using the CsrfExtension", + "symfony/security-http": "For using the LogoutUrlExtension", + "symfony/stopwatch": "For using the StopwatchExtension", + "symfony/translation": "For using the TranslationExtension", + "symfony/var-dumper": "For using the DumpExtension", + "symfony/web-link": "For using the WebLinkExtension", + "symfony/yaml": "For using the YamlExtension" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "tracy/tracy", + "version": "v2.10.7", + "source": { + "type": "git", + "url": "https://github.com/nette/tracy.git", + "reference": "7e7b25ba103968d5318d37db330b2e9c755dc765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/tracy/zipball/7e7b25ba103968d5318d37db330b2e9c755dc765", + "reference": "7e7b25ba103968d5318d37db330b2e9c755dc765", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-session": "*", + "php": ">=8.0 <8.4" + }, + "conflict": { + "nette/di": "<3.0" + }, + "require-dev": { + "latte/latte": "^2.5", + "nette/di": "^3.0", + "nette/http": "^3.0", + "nette/mail": "^3.0", + "nette/tester": "^2.2", + "nette/utils": "^3.0", + "phpstan/phpstan": "^1.0", + "psr/log": "^1.0 || ^2.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10-dev" + } + }, + "autoload": { + "files": [ + "src/Tracy/functions.php" + ], + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.", + "homepage": "https://tracy.nette.org", + "keywords": [ + "Xdebug", + "debug", + "debugger", + "nette", + "profiler" + ], + "support": { + "issues": "https://github.com/nette/tracy/issues", + "source": "https://github.com/nette/tracy/tree/v2.10.7" + }, + "time": "2024-04-29T11:44:00+00:00" + }, + { + "name": "twig/intl-extra", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/693f6beb8ca91fc6323e01b3addf983812f65c93", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/intl": "^5.4|^6.4|^7.0", + "twig/twig": "^3.10" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", + "keywords": [ + "intl", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/intl-extra/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-11T07:35:57+00:00" + }, + { + "name": "twig/twig", + "version": "v3.10.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "7aaed0b8311a557cc8c4047a71fd03153a00e755" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/7aaed0b8311a557cc8c4047a71fd03153a00e755", + "reference": "7aaed0b8311a557cc8c4047a71fd03153a00e755", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.10.2" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-14T06:04:16+00:00" + } + ], + "packages-dev": [ + { + "name": "composer/pcre", + "version": "3.1.3", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-03-19T10:26:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:15:36+00:00" + }, + { + "name": "helmich/phpunit-json-assert", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/martin-helmich/phpunit-json-assert.git", + "reference": "5a3a53e46f4d1f1b324c5cc2e33d87ad2d37260c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/martin-helmich/phpunit-json-assert/zipball/5a3a53e46f4d1f1b324c5cc2e33d87ad2d37260c", + "reference": "5a3a53e46f4d1f1b324c5cc2e33d87ad2d37260c", + "shasum": "" + }, + "require": { + "justinrainbow/json-schema": "^5.0", + "php": "^8.0", + "softcreatr/jsonpath": "^0.8" + }, + "conflict": { + "phpunit/phpunit": "<8.0 || >= 11.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Helmich\\JsonAssert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Martin Helmich", + "email": "m.helmich@mittwald.de" + } + ], + "description": "PHPUnit assertions for JSON documents", + "support": { + "issues": "https://github.com/martin-helmich/phpunit-json-assert/issues", + "source": "https://github.com/martin-helmich/phpunit-json-assert/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://donate.helmich.me", + "type": "custom" + }, + { + "url": "https://github.com/martin-helmich", + "type": "github" + } + ], + "time": "2023-03-03T14:09:38+00:00" + }, + { + "name": "helmich/phpunit-psr7-assert", + "version": "v4.4.1", + "source": { + "type": "git", + "url": "https://github.com/martin-helmich/phpunit-psr7-assert.git", + "reference": "f35fa69e07cc16977b52805d3abd873cc16747fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/martin-helmich/phpunit-psr7-assert/zipball/f35fa69e07cc16977b52805d3abd873cc16747fd", + "reference": "f35fa69e07cc16977b52805d3abd873cc16747fd", + "shasum": "" + }, + "require": { + "helmich/phpunit-json-assert": "^3.4", + "php": "^8.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0 || >= 11.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.4", + "mockery/mockery": "^1.4.1", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Helmich\\Psr7Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Martin Helmich", + "email": "m.helmich@mittwald.de" + } + ], + "description": "PHPUnit assertions for testing PSR7-compliant applications", + "support": { + "issues": "https://github.com/martin-helmich/phpunit-psr7-assert/issues", + "source": "https://github.com/martin-helmich/phpunit-psr7-assert/tree/v4.4.1" + }, + "funding": [ + { + "url": "https://donate.helmich.me", + "type": "custom" + }, + { + "url": "https://github.com/martin-helmich", + "type": "github" + } + ], + "time": "2023-07-26T19:04:29+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" + }, + "time": "2023-09-26T02:20:38+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.0.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + }, + "time": "2024-03-05T20:51:40+00:00" + }, + { + "name": "pdepend/pdepend", + "version": "2.16.2", + "source": { + "type": "git", + "url": "https://github.com/pdepend/pdepend.git", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "shasum": "" + }, + "require": { + "php": ">=5.3.7", + "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.19" + }, + "require-dev": { + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "squizlabs/php_codesniffer": "^2.0.0" + }, + "bin": [ + "src/bin/pdepend" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "PDepend\\": "src/main/php/PDepend" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Official version of pdepend to be handled with Composer", + "keywords": [ + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" + ], + "support": { + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" + } + ], + "time": "2023-12-17T18:09:59+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.4.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + }, + "time": "2024-05-21T05:55:05+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" + }, + { + "name": "phpmd/phpmd", + "version": "2.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0", + "shasum": "" + }, + "require": { + "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", + "ext-xml": "*", + "pdepend/pdepend": "^2.16.1", + "php": ">=5.3.9" + }, + "require-dev": { + "easy-doc/easy-doc": "0.0.0 || ^1.3.2", + "ext-json": "*", + "ext-simplexml": "*", + "gregwar/rst": "^1.0", + "mikey179/vfsstream": "^1.6.8", + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "library", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Manuel Pichler", + "email": "github@manuel-pichler.de", + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" + }, + { + "name": "Marc Würth", + "email": "ravage@bluewin.ch", + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" + }, + { + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" + } + ], + "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", + "homepage": "https://phpmd.org/", + "keywords": [ + "dev", + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" + ], + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.15.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" + } + ], + "time": "2023-12-11T08:22:20+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0", + "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "dev", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + }, + "time": "2024-02-29T11:52:51+00:00" + }, + { + "name": "phpspec/prophecy-phpunit", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy-phpunit.git", + "reference": "16e1247e139434bce0bac09848bc5c8d882940fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/16e1247e139434bce0bac09848bc5c8d882940fc", + "reference": "16e1247e139434bce0bac09848bc5c8d882940fc", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8", + "phpspec/prophecy": "^1.18", + "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\PhpUnit\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Integrating the Prophecy mocking library in PHPUnit test cases", + "homepage": "http://phpspec.net", + "keywords": [ + "phpunit", + "prophecy" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy-phpunit/issues", + "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.2.0" + }, + "time": "2024-03-01T08:33:58+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.29.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" + }, + "time": "2024-05-06T12:04:23+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.31", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:37:42+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.19", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.28", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-04-05T04:35:58+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "softcreatr/jsonpath", + "version": "0.8.3", + "source": { + "type": "git", + "url": "https://github.com/SoftCreatR/JSONPath.git", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0" + }, + "replace": { + "flow/jsonpath": "*" + }, + "require-dev": { + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "autoload": { + "psr-4": { + "Flow\\JSONPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com", + "homepage": "https://prismaticbytes.com", + "role": "Developer" + }, + { + "name": "Sascha Greuel", + "email": "hello@1-2.dev", + "homepage": "https://1-2.dev", + "role": "Developer" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "support": { + "email": "hello@1-2.dev", + "forum": "https://github.com/SoftCreatR/JSONPath/discussions", + "issues": "https://github.com/SoftCreatR/JSONPath/issues", + "source": "https://github.com/SoftCreatR/JSONPath" + }, + "funding": [ + { + "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", + "type": "custom" + }, + { + "url": "https://github.com/softcreatr", + "type": "github" + } + ], + "time": "2023-08-17T20:14:00+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.9.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/aac1f6f347a5c5ac6bc98ad395007df00990f480", + "reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-04-23T20:25:34+00:00" + }, + { + "name": "symfony/config", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "62cec4a067931552624a9962002c210c502d42fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/62cec4a067931552624a9962002c210c502d42fd", + "reference": "62cec4a067931552624a9962002c210c502d42fd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "5b4505f2afbe1d11d43a3917d0c1c178a38f6f19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5b4505f2afbe1d11d43a3917d0c1c178a38f6f19", + "reference": "5b4505f2afbe1d11d43a3917d0c1c178a38f6f19", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22", + "symfony/service-contracts": "^1.1.6|^2" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<5.3", + "symfony/finder": "<4.4", + "symfony/proxy-manager-bridge": "<4.4", + "symfony/yaml": "<4.4.26" + }, + "provide": { + "psr/container-implementation": "1.0", + "symfony/service-implementation": "1.0|2.0" + }, + "require-dev": { + "symfony/config": "^5.3|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/yaml": "^4.4.26|^5.0|^6.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "e6edd875d5d39b03de51f3c3951148cfa79a4d12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e6edd875d5d39b03de51f3c3951148cfa79a4d12", + "reference": "e6edd875d5d39b03de51f3c3951148cfa79a4d12", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/process": "^5.4|^6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.39", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "85a554acd7c28522241faf2e97b9541247a0d3d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/85a554acd7c28522241faf2e97b9541247a0d3d5", + "reference": "85a554acd7c28522241faf2e97b9541247a0d3d5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.39" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T08:26:06+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T15:04:16+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "eappointment/mellon": 20, + "eappointment/zmsslim": 20, + "eappointment/zmsclient": 20, + "eappointment/zmsentities": 20, + "phpmd/phpmd": 0 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.0" + }, + "plugin-api-version": "2.6.0" +} diff --git a/zmscitizenapi/config.example.php b/zmscitizenapi/config.example.php new file mode 100644 index 000000000..e22d07e57 --- /dev/null +++ b/zmscitizenapi/config.example.php @@ -0,0 +1,17 @@ + + + + + + ./tests/Zmscitizenapi/ + + + + + ./src + + + diff --git a/zmscitizenapi/public/doc/assets/.gitkeep b/zmscitizenapi/public/doc/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/zmscitizenapi/public/doc/index.html b/zmscitizenapi/public/doc/index.html new file mode 100644 index 000000000..5537aa266 --- /dev/null +++ b/zmscitizenapi/public/doc/index.html @@ -0,0 +1,23 @@ + + + + ReDoc + + + + + + + + + + + + diff --git a/zmscitizenapi/public/doc/logo.png b/zmscitizenapi/public/doc/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1a1014b9e3db5d806ece2d7183bc2d053c0773cb GIT binary patch literal 26155 zcmY&=1yodFxAvi>1(A@F7Lgpf1_>zvQIG}^7?AFU0TZMIq*DYzKtOWHp`;t>&Y`<& z?is)D-hch9#TtP*``z#E{p>x4KGRetCAvoh0060~isEwszy|+`4cx*9Uyi*0UVtyS zZt|)+x4&vfu16f zGV&h#Umx0EL!{b!D2>slYDYsH07bL_r0j8{YGY_)BX50Wp0|v8LNRRp^T;EUhGbp4 zLvC*F9PfaMk7)iJ1mdfyOpWs)v_DsQOtT4IM~k5GZau6!D7hw3HGT%%rXa+DbYu5n z@!{ax?}*?jaXh`0i86zyw3O5NJkqaAjXAb5k~yIu*!|P1m!V~AtCm@S?~RY>J**S? zcFzCBI~TamN+}N;#X(RZzD$PtHTCaL_4xS9Iu zCTtP2%$LotUA;lko^jLD4&Epacd1EFSGi9I3OW_z1XWhqy(dg$w; z`B*sV9y}d2Gq;SRUY3J-L8T;S>Poks$Sr&6^}d=3!MRnzWcgd6SaYo?)Q7R?8Mp| z0}}5L6)20+tcL&)uA3Pa)#0`Xm*nddc z#;?OWnjBbtCpdSo=H}!u9B>)ou{D6i72DmHb_y7ueVWXO2bu@3Kp7Gz3Y#1Fv#DNv z*hF*g+q_(YJl(0aXZF@t+UA9`vU~^SQA!7k$Ck!7W>~v)Se16HyX1Z;d|-O>QAJBV z$H=k0rs_nxSYXTPVee-Qvz$GJGJ9ktW|_OcPTLPU+?*B<&)J?UczSXYn$Eob6~=n- zW2o=}$SAI0nQS9%rkI>=MU)?Nzz1?@*p^!DV=g$by}`wT(Oqe9(l(htNjZ*widlz8 z6|}FM&W+8qd-6)&*49=2B1&Tg<0#b&=+Qmr%Ui|1^cw4aZxx*64n{;;Q6#R*>v3loWw0Aam zaBqAPN8-fyMmKu|`^9$+b^MMd_8L<4*QY;jwKl=gJy zrUEDUMrBE_&0OW(-8yf6I=VN{YVXYdPDcf7As&U|+{3yZ4g1q$h+|f=uxz${xWDU{ z5z!S3ZQQ89lRoZ}3Wj0t{Ycv0Dq#=3LjxuxPkwaxga% zeEpXO^p;jR2r;cStT<^R6c)c;A{=U7gA3mEZPK>fHa64*I3NzIK4?P=L7ct~*Hr4N zxl(FPU-Em>3BMZ7RSVYsyK{H18s#%682mUZ9lUs}3}lM@ENC*+{gKh{kKK;mK^)zB z)x3qlhL%>YOCzY#{+)NKTIw^?>eK)^7QatAO@jjw2Op?<6=O)*aBxD9aL|MsjmsYm zw3SI<^i*^5eG!olGM*FZ?@ngoa8BstU&1ba@EZ_I<4G_0%qDl}J@_q$C8xL;0|vwG z#$Nlg!b_hnlA`)!LACC#v{7m`Ztl*l+RIm(;u~@&45ckUv=VT%aJ;W& z_I;eXGR4v)I|{fu_ulbYw#v_6I;HeMn$C!Ceq|H4N6Vc@)IQ!Yz7?+OHaMnpy zqyQZ(l%Brm6iRG~^Rx0ok9#zCA6LDt6#jGRxY=7swYVNws(8D0Rl zqZ47U>c-l}o#2KjS#+cy^Cfd!obC=$m=GZ$VXE&Ii*eurcXl>AL5+HFw@km^g!!Ar z5R3vJL8h2#3Cjuc@=x{~?-U>0a4n^d-tE0O>RCyr=l`Y`#n&{`kDcI~j`Grp7yrjw zXOAYHDPGJs&)%y|M|kRb*=jZKBmC?iKb(*M`BXJJSnEPI87rsn0L^_ucXIM#9~eC|Np^mVs3N4^tlO}p`KPgRh)YY?1_smS^N6W1%4@^im5NxJ#oQz+_N?%Y5p zRIz5ob9sUQ9p&j@h+ory_0cz7Z=vK7;dk6IJTRt4ByFp0+oLUm$YrmeaG`G-)wy#d z%D!doqzm^LzNZ`)+&D|_qGppGuax!$X2}_jQTm)3AM+lJ;DomSNi7x5kzXzN`sOK zbZ*Mt9e@nGga5(eN2!dE&6%sLKjg82OPvX`WWqA@cOjcQCG3Pa81bpWD#@K0gXVTo zA8XTJA5UVp>xwWJ*F!!!aoyarseE#XwHOJiUy%{a=pW0>zh%&vUVbtA(d%lY1F?Wj zd!i2u^(#aNDhQk-T4LB+!LZ4GZwBWGKT*&Ri@*LnddW38cQNL<<4r%L$&JrZuxysk zE@nU*)OFr#*t;M&prfnnwt3yZaB3F^)%$Ui-d@WbaMuO~Xh}U%mRG^Z=p*RD7IKu0 zJZw+OUhTV9DG_Sboq6tEg(>uIkt7_e*kH6OED=13XQSWXO4_0v?r9tee2tGg1nm^x zYsMVbS+o&702U_Ed9PrEjlY6V;2DAMy1E{Ix~p1J%=MHkJ!FDjydR9a*cjRIYT|j~ zo~=|(_#a;LM|MYR$Q(=?HSq|{!eDfKo6$co%-gsG%r~hQ!`Dj5&hJ2A{PUkO;KagG z-?W1ge}Hb;mo)LX@l~G5j@RPydL&N_^sr~|^leIlVKf<6+Iifv1pW{AHxF>)@E$`*mi!hQCLz^gIRsPnV3T?Re&sb{^00D~= z|Irm1i6WJIw#`~E-lY&!gT$3F#3!J8uNK*PukM7wdJH-)h7RNo8%ZxERKf@N)fJ5; z?(f~VF{cY<2}DBAyvC?$mrEGl+y|}I#fm?#KF5ACyt>FP`bn}ev~GJRpX`k|#*oE9 z1HHiNF+zMLa=4@~gY_BEPESv0(+%Ci$gKIq{_LtV1=;GtJw)Y3^Y`+4d+onZx_on* zZ>syCZZgWEp=12KD_`V1_y@Ke38-cY8dn_hmqn(iQZm9|C3QV~rb>Y_Q9iq9hkp|I z`|DnnuS0ORBs;Dsq05Z%2n#7j_*I;TG;_7F$IP>=+ zfY!R^tmCHMmqi<2KK?t!5c_Mq?z8Q~>LR7&yt*tQMAACQ94+R?-|+7@9U70Zu#|L4 zk`V!dn9O9nhr`ak!S;a3o^q*2^0Ng+C{(fDahn;8CYi5Pr9EXMw zj{vHngx)nIw-1M&gG%^c8u~aDY``zqQj;-OjlVxUG@acl3M8}5G3xuU5=`JQRqk%^ z>AhU-1+rxYej*vlhY_RvjB1t%{fsEn=Hw`v@gdN93&W$i()?p7%(XJ-zD1EHc(Gq# zYD?mKSRrhWv0mk%d_?}A$RdDphuE^5G1af?s+z31N-r~iE}&_%Fk-X8y{}pBFe7yK zvhS{u?=MA~f3)%F<8ek!fZx9yj5L67U5taeq@#3k>>jfLzl$8MMF3&!+X@)A3ULb2fM0S07g6U!OsPUQX4|t!yRvFkW!Uu0hg<;3aeEu;P_(NeI;}jlK(Vld1 zmlf}j07f=l&5iFs;tmT|l)p-ahTlY|(b@31>wgx03&u}G>HR%9kl~*%2u#pXR+#s{ zNBMk^W06aU1m&be1pL;(uw!_uBBhg^i$j7>>7H$Dt(Y*DKpVz()QQbo41w(mYPrJo zzYV5<{+@Ml>>8&0&S$ z9QWU|@E-y-p6iIX1U2FHWTV~F9N_jljO^+94{d|a0Tane4sO*c?Do$ZPwc6H*IzIP z#uI!=Ow7{=eyyU17nF$coFa!*f-eQ#!0#-hYjsgKlM$APf4|1lz|{dl?mkWc@(1n# zf~pva7}0xwnaZi}mb9ejU}wj5nlEnyCGL%pV0aQKkWN%Cf_}_i_oUEFO2Al#!Ye0$R`9o9xYlSMMPEmsj$qbYk{fB-OxqBL(@oowG8{B@dX&#bJ zny_eG0m=@j527}AzFP()E#+skkWE|PI-T*AAWSEi$MHOtcFJEWwWNQ8E2qfX*HeQ< zgY#8S$8~8&YkisdT|>G+lHTmqgMp{mj$Zna!vC)B%=-mx%zNTt`-+N1{oci= zLQ|vLm|!i1C{(UY0|lh`HHiZ|7h6Z?<^JSUhGdaSa#nu6WC@pG&UUKV{Dk{H|_l2!nG|l2#M`0HAG#mcPRxZ@6%b0ppP(QJ7ldh2M;? zrcAIY81Nl1mJ(g*HsADh-OYHEh2^Nbe4?p04ZY<-DFf<{eaI72ExdLJ{I+SxuDIG` z#4ydUl}|R|iSai<*eJCPfq>V7Q2U6=gOy%0%4r5Ldj98+s=si^!6;u{s333OTl01v zMz815QHd#?RJsVI_094>z!z1OtaSH=8YM7ZO@LuFyXKC<8-r?)Qg_$FLe)V?6WlmQ|8(#5oSxOhR{Wu3cj zy_i_}38RQfwIELiqv|3B`MiMZITX?V-J~0*qpxe+eO&*w{^@jNQ*RyAFHMpBpU`V! zTR6E$L(NTU|6ZEdCgjp$O5f<0pp$U}tr3|5dhbBT!-N1u!<$cLXOM#-HEI&zYKZcBl{wbJY|o{ntoPKAS<+0NBQJiHQ7h zQ5w>Dhkruocr*XMnPNdXC_7sb-(k$!t~l;NRd*&{_V0*HBdH&kd!#G-a^i4Z6^|b#OSeiIvJhE#km;jY3`Sd#=Xp_r$@w+?n8mG4! z*JLt!Ef?+r{$2mo!^WTn8S$m}EsP0lq|sl$c980&-kJ75eneY@fl?A*@JYwgD>c^$ z>gE$qs`r{2bkHu;EdD@C{wk2z zm@44mhkB~-$wP^Q-DLVN+e~yRAF37+e;GH2o#Mza#5-4@e7YGL;ANJuVt|fp_JaNu z^uE>Z41vpdu;0P?*rW|#j3Z((bv_ntGv7lRCa%ix4EXX$F~)nom&?cKC*AG>gH=HUB@D1mezdI zP^yiVo!PIeNofuAGzfbi#fKH5QeJ=^NYJr86dZn3{;SS7*;X!Nr%8i%pFS6(1?s0b zDtpqtr)!Hf@rbc1y{h?D9X$^fZ%h~VD3RKGph{0K!9$q%+IQ?aJfPA2Q;y2N4((Ci zCG})(F1DO_4m;Q2ce`9Uw@62Hm>DJ#5pO&Jgee5-E>3oyEVij84p$x-hB{#WaxC3% zq_A!4PO>!?dsME|e)mr?%R78rvG&@0g=UO*zW(Elacxi90YA{SpG>}K4{Yk)1zmdq zlQn<8mY;9xJJxzm$x4{st||5R+Wv45w-JHqLw;vDDm7-tetrL}n90N@I*!ifX3fVm zqT83f4s*|!Jy`MBThe*ZB)av;?ojCEwt-VwKR=j{qrZVq3pO1}$4Sia+&;8G7a#qrpzT4gFvThE%nQrsfYwYuI7PvC$7et_MmY( zR*jc_h6%B<7BkJ5ik_=^nS{Z>-6HNHgbH3AMV;#O{BPII?5-f zC~W*|QhgIMTDBki1Rt?&2Deyy?Dt&Tpke}QX8in#uC7R&t}a-hG&Xba=$BjK@#qy3 zJ1uCh7RE-s+B*rr>6M=6{In9n24?K8p3-3R#-_$#xm{j#=#xvW#KBbyKuc0a>X@4&>6LTUCE(F0Rkw z`TkV-OElc&DLHU0EDTRRaJ6X^OWcC=v;Pj*;{9bP?Jq8JeqPiaVjz)V{5B!E=9uP5 zQgb_#-N4-L`@%I-3hgPY_JN%T2>+n5C^>pIZr<8l@^R%PM1-o6aR7)aLV!1 z?-W{>ZInPJ?J?)!f3ZRiCPe4agm|%S8MZ5&;5)owr2r4ok7q3qRq{6ZheI0EJZSbhgG z$S%&;(+Zc6i%p;7LJp~GepXQw)h|Catm+~ZhGH?f=tn9N{=&I!)niXSPb2DfP@F3C z!9#3Xu1V(fnS|A6%^OzpXEJbEB`ujh$vj1wTVeb+d%K0|xt?`rYeR|6*j z@5Yq%aZ)#Z(q*L;*?_^;o{!kO&-~DVX~}P~KYt?6UHrs6O~pAxaz0j+b9Rp6W)_P~ z4dY||X;f8%0(}s%bP@Juq!iDo?lP(6VbzY4^~e<00$=~(C?a1eE9?o7^&U*$hAtG7CPY!`4LV)6W3so*rLpW{SGJs5z}cNPo#q{i%DJ8T)6Qr>QDU zCSKYvYW8;|`VSH7`EC;yOJdu36i3@geA=B7s!(|%fGo)Xe-CRwGB387D?QVHT3pX6 zw1ZlwH(BY%p=yK}D>O9p3bm@fHvK!TrE9}3I!zTR>ro}Fc_{5v0j7?N2LQr@xx~U* zxzor=d;j`0vWp_xVZo>_F^elbuj-a|SmDzh#0@K@C|R}CNP%C%yrFQY%8eXWBLzeX zKnNp>ucn;18AZA@9;h8IbxvCfRGYWsY?@@ zX+7{hIvQi8)2zQckdBO-a{*PRb1uqTTHUGk%6CW9ikX4(^{A_0Y`JAdQhT$pgG+9o zLk+Ky5S7@tfD7&$kIjwA^*29LJ*U5s@#NP-SX8!ltsa3Bs@pzra>uXmL(-d(8YyM= zcmt1PX3jk(ujLzdcEn}K@yHy9;2&K^%6ck*wWCK8IBuZ+8hG*ZyR9Po*RJ-60w2WZ!rIDE5)bX=ZA_(0T8){v1ps zDjnz7I=!g|#dct~Ai>Rn%zS{DXi}x19C0VS?+VsH`G*IuU2>6|LGfgB@T8Vg$G$!e z?}Ckvg4ql8EM+kCW$)Cvq!p*nOos!@H1W7%Q&QaWS;?Sw{t^QXE8Qj|gEr}?2oIBY zieLhq%*?_PWt`ZY_*E7wDU5yJ=({=)JuHJ*FsmNYWn`s%l5#V(^S9wwtmFal=C7Lq z;66N)BB>7G!E&aWj2p03pTMAr!QgD5emE7=p@~;z<_#B z7_0_E;xji-@qIQKQc=2Ks$kl)b}Ho!f}jhC`}YZ^BI4UzCvz9-mTrZ0b*55~77aiE zLd|E;)EFp(Rg+@y17!A&+{PLuco8&JUcRf&$B_52x-UoVa(~pMK&xMA&lrEleI;f6 zK}81x<`S|@E$=Xq_iJ;MIIGrowh;l(!8%t(=1)H~Dq#oKQOf<+>vil$9{;_^Ax#|K zyy5DU?eD6IH)f(sX!i2IKW8W622^B5+)y=qj~Vik1zS7_De+@-dz*Jp2`cerZS0B@ zR+SRKarB}NI4%TZIDYbVZ%=O)E@d28NGdLl^S`jh6FYU&{iMhY)>6^pC&R_Ze7mV2 zOb{vy);g>%TPSXbL4sM;qrKm-(KfII!r%^yi;3Xm;m|3NMEFHeEJHk-wjx+QvG)a> zjI_?L8ejqd1{$P9{pe3_2Wt}S-hf_veh~9_$3S#%@IN9&BoRJk1(NK(7?eyHCnMk* zr(ld4H(5Dx;)HLL;uLd8a&W#aGYGbG4jcttVh+Ke4cSTF@FVfjLLL&~Pf1Cmcs6bS z-Wm$_XglyLc$0}%vH_3YaflIy``(VRJDi6t*>l*vWNXjm{xkXSnMR)#fa42-WH_X( z420et|M37(k1t+~L^se*AEzdfy68qBFtzj$NyPaE0SMb%%0cdWclS(6K2rTxD`qFv zY<^2fl=0HeoFF(;i^WdC;0n<&u+>aX+3`tk&We#6i)>VZsC($gD`G}*Dv(O&PNE{j zJ`$X95Y&)i%?gxJ{dc+*BJ2iE6Xu6DnKKLPSO+|?_ac%iT=e7LCxH)!2n38uJ0dn! zAMzAnoA_a8q!)^l(GeM&GX?@-$V(h;SPfV&37>Fa1ehn*5H26{5#r_rZ>@qnh zGj&1RLCn2d1`+tJ zq8Q>6Sx)e?xWi%u#Ct@J91${kzoY={mANsLEekLXBatxI=T;mEPB;i^st5uF%9MK_ zwVV6=gZ)`cHT-3v{nxtzeP`{+Bth%PnNoxZZ2ea-y<~%|TB015f46(Q%5U8EL2u?y zSTS5hSJxx6_U_)T3Z7d#TSbx+FERQbdZjxen;y7g5-D?OD0}`I8%FJI%REm5p|^v* zV~l@bgi$eTL?yU!Q8j(afHHL<7`E;u4&1nH`wmpy=2D*Y1UBYAZ%XH^dr{yn!D>T0 zx_$DhSJ|Pxae6OVm4V=&Ew3TwJ!08Tduv)JDAHgweGF) zb;H#J+EONTAhjJmLo>RQbr=&@QT!UGhk-TS7SG2H#}C=wqHX~s*y(t%0+Ez%K}2D4ox(#UaO5@7ZF3Y%XMX!2aDL6*E$9%AIrGP!e-l z(IDSH{7ToPWsReU;{y|I?(y_`VRfbEirIz|c7nCN&-fv0BDv z8|TSrrD3POhAjnu3L=;8FK{t%lODJQKB5=_t_@&TAr?Z0iSIitlGJmdG8n4p15t96|an5W?0NN=QL?~;X2|`Cry;WM*B75{$ccr zSuZR4+t+^I5kGbkb6FViI-F_cZlWi*!lKdkLp3TJYGs-W6M))z-3)@Qi9!KnIG4xE zJ+U8M_X2D2Rta&bsU^F5+JIB<^wYHA*A_ufFw*+blUNpn@pm>YJ`eWA-RQTC=a37bkN*_SdEpWcWtJ6^!9QWoT*1u54gjiAjB4DFft+ z;L(N#K6p;`(DPRVO-%xPd0(>8?cfurZsMgEd<0@{z;+KSMvx1J%#C}u_F5z!m zv+ZVi=E(E->yzm;*n+$#?w5=YtgppO-%;I8$Dv7@_ywk6PraW;_lPJc zq7gb4ZF);wE~`+-g$t>SS2tO{eqU2iRP@Po@~9|SAtGZ3@=tFwj_D)bCRd@-;d`E{A$5-9UI4!%_(F zL5V~9rdg%0;Yc*_+QD=01p!uU&(;%&x{Ho!2gb!iaKT~Z`>>*O@+#;aHn<`eyhAeY zI4=f~YBX^9PiQP9ge-mUM=m9n6`6zHdv@&+hJS+7AX*j;8=W){)xh98tOs0OJP&=Y zK13UPAJ3!|F*^%YRFSrAv4T}TxKC(=*_%r}k1|4_q7%Sk zLmS;8Q|;z&jQ$C72vW42w#m=NFxZKWa366<&0CC#k8FsXUoDxPVm49U))oUdG!C`IoYj1Ci?MUzZ;9aB z=!e%|b;p2b>tbC2B5?13C$8AsgiL?|H(|M&Mz*dY<*y;ieOANj5S3va-AlyfLP8R_ zv@m8CD5|O&3Rq(Y^lZ`gAHJ00qX41OCwWKo#GfA;=ot6L#5|0T*C}%k5K&jp*Vo?o zp4qJB5(nQTn0x|Kd=rCiP)@%hI(hc0D~!J}Q+e<_^^zMfRFHbf-Ac zXEns($1rf^32z+dtgVL(1cCQo-gu95105L$DS~b$94}rb#OmpHZoO4oUDhkfL)=5R zO%$2a(5fby>HKNE3-_0mX!>rgNpx+LJ5*)$5PT6tm`H*zwnuV`srSUpg;1t}1X0r- zB59K4c(g#cSQ(B?e@ayxlnKmf&vQ$L`n7nv&wQ-tphXWX4)TT63ee)l%ntc>;SzXp zJ=}SPV{<|x(KC{;5~FsKleMo+MT|lnPe;pXC_)Q;h!b3!z|hw!Gd5J9OcPYph@zJd zDvJj2g2ZFSix}xXG1c@I^A{Z)jw~LB8$G{7e)ywnUdVSq#B&{PqSHLCR59hl=`E0F zB1y0ebCDZ+bh4GIYM9k_OOgHRWpO8%B6k-%FN-W*A=97LMg(BI>I2BQ(MYS=4_O#sK>gs+Dk9xtm z4M@L_XYta?ilRP{x%YFym46)zNe;X3Rz}Jn!Fo6GQaw?Jp(8B?Mfqay0B&%T?smWI z8T@E%lF(M6u^m>K0M2=%3zafl1>4j$Ij`s6bxI$oP)fO<4Ug$wL}*b{9ZrU0H<&(` zkLWu$(pjmh>Bd7dpj^D8?ECJGCo9xiN3X351&6%vtGQyu>;*|*&Xc+qlVafnQNTod z4yDbqH^BCl!t^;M*wiVmy0k}+|LDzV*f|xks*^1E5?w~zS$u2emv^tKN5n`3iF?n} z)o?6c3AfspM~-y5cHSW>Bu3!Mgy7*(& zkK&b1Ri2LSxe~65j|DBT#m~<*FA-mxwuh?Sb1NC<#FU9|hpNQ(^cZzqN1>yPis}Uh zj)Rgk4yAWDosm`8a#U&?lX29jRE4fcDaVuZgN=KpiKU;F1C5&|OE+%malIJ(>NYse zF)irAF}5?A4ym%sxqqKE;$*>kE_x~l2bB>hEGJZPh=mWrzDYU#^2#8a?3UKC9$nm) z4E_S_H~aDBYH-rxrzX7$cM{56>ou>6czJ)W<`*aIcPxt-BmMj|*XN$!!B@)GPWM@n z!9UsTOjqy(YzU<7VC}x?G?m=bhSa=j+1s`J4$QtG;VopBEuC{tZF@fpCeEvW8lV1Q zO}qNu)4cfbX8+Ig6Mwd-Qxx?#dOpSMZ{Q{?;`j#D!T*TNolzr^3-=))9{;x*^2-UC z7N20&Frgaw9fm5q39UCr{s#v<;@kBSsfUl5+;Hw(v2#8(dC{l6+mBlO zt*-xL@E*mRpna&O)zLU%FpTHEy%)GxCms`UGEpv$zJgAd>g^OPj5@`ajHwTwxjP1b z-^l(+LMvGN#l$S7g$-HQlpB|*Xh|=#w_E^jE>%642Y2{X#AlVU8l#s@>u&~5kDFON z;V$>0t`MoVX#$J#pkYikm~}Qd^;!+(3yp!e0STBe`XDWH#5*{$JW%*sOme@(%h8*g|hFd9K??&G=rAr$F?d7dl<{b{1Z#kugXl_`wZ-rO_!ZYek@4 z9uC8LNQDV-+CLtg8QyyERFu7fK3Hm;-KLkPaJe}OwcmX}$yFhuyIHPyQK^=rVMysP z9BY#it(bD8Xqd|pos=r~(iNNc#huQln)lfxf>XaL?&TRlp(QUPi`()}=<#v;-igRX z&{>U$=$u8wJcQnr{hpnVkEFx98~8HrT;FKNLg|f&U3b7E?w>H9@ZQSP5!%z6^3S0> zYCacVj3uo&Yxz_avj*&T{kyKKjG1gq@xzHme%}Sa(oQH?Z@fn&r3yyl+KaF%{>!dh;?L&+Chn$Q{l$B37i%A;5y}Kb6BYHI_o$*JMPT6<%k+E_X!bltmHME` z!4=M7YmHk;6DN%}nSRdb8rO_88JJ7&d#fA|v$S7X*tX3A^wWp;>KL6-g_fE-V=)$& z%O!4?M~G{6LGYHRho8Nn#a|}y1|@bEK?bD*RQQrQe^b=Gp2&CW|I^j20H1GFlx*04 zVls?6EJIOHOf8x$KARflrMqrZU?1hXgKRvkgU%|?-nxf%3xrB;~-ApgxaJt;1Eau1JKs;>ff zfSY=20!eG7e)hVg&4=R4+tp8vzg^=hW{)BWW8bB)RMX30Wrp@_J%1(sY-6=4=+xL^{V?>^+Yf(RPXU z$>2*et}^mF>F;$lQPWXZ+Y|o>DF!Tx?d zZGb=ji3ev8c`l^-V0GV!~I2ctHkX^RIWNedP-YSAUI;6w^R0Qx0gSyytHRG zkJGnGZ3%tSxzf5ld0R=0zV$;ujiu1VnQ6FDX*zv_QB$G6*d}3kvQg`#E3*^nCIw^@ z5~8xU+-pQRJ-u}!MEXrD8egEyD5NwN?(2$Ob(O<&r=PjC6UXC#JQt@1wmMtqrv6ny)&tv+ z81J2dqB^*}man9K2`V?vD)nGczDt<${!n4z1TA2gWY89GS}Q^X7@5OzakMDQ?Ok2s z<|4hFOxMeAWnA5niQ@6xZOtYoaC4t~8SQU(euu!bck$EkrRxaaHfuxXQ0rZ*)csD@ zhglXs2`NH@D$bRfdLuO?;;|d46DP15yK%2myZl8w|6UTS!3`-U%+Y-l-2U#9p3^>Z zVnXGb{yM7aYV(!#QU~jy^=mUirEEe=@r9^q!7vZ6AQgyqUBfKVH{N8|M`@}ztsf%- zW^p!T2YLKO7{qMNC%n|hl#iC7g-$%2skLmE?jxSR2klQI>B6DE$Z2-QIkR@9V2=f9 zqD-d`Z?z8$fpzk^}w?R*bUl=36XvvHZkvk8is z=b}c}Yb+qt*<2iZ^EYWeRyI{3v-zi{rWBxEKYG2(#R{UI@1v8fu)YUrHeI8ckfn<) z)gLIN&UeRU-|hBI1c!O9->#ZEY%5#3SQgkn3M5${$d-^KJ=h4xPEW59#gK?b6(A%0 zV;krm0=R;Zs7bpUDZEqUo>AZkJkap&@0Eb4t>Mr8eY7e_Q4J!mMSVw4A!f$0SdgIN zmWJsKT|D2`Z?a!oNRTSi#$|_kyn`QTU>N% zBacGuen+s0%IC(18Y1{8cxB>lye*G8#e@)E5NK1*^K(rBkVBqp__e#X4?Cwm7l*gB zyA8SHcy8FotEGYJsjAfxwwh?VU@7tZt2Vq0e%OR>?$q-Tm9H-VpvV6Hp6IjF3O|SH z{z!i>Qv+4qGpAC=*!0%5;i{##X;SYmf3<#N^R1i@i}fAmhDdDfR@C(5et4^B8I~LX z2$bo}i)8LFNg?U1;WY?*a~IulsEnJ@-`P2&HOk*mI<`_lS|wTuQfYO)sdA6xIa}DHvQDarP!=MM;7{9?Aq`H zd@!>Lc6Tp)hvas2ugxe{V-Fc#5!sHPwWhWQJM7A7Of-$ZZ!+D{up4Qxks8;7;yRA( zaCnQ!$nUL^sUEN%i%bEpV2r_Igco!@yoH4lPEXIQuTHyE7iR8U4$5MWz2)$&c&f3y z{oPLhVdCv{dNe|diZtliC=KDYU||)^oWKX>=`u|+7sS9IOr20%Otzbil;wRv7a-4H zW2qcSAQ3A2YnbwU50Sn#F3MZDTlQnR9oaDA4^T|x8Q=pDDPnO^#O_Uoo?c*Cxp1Lb zG*~~aEG_vxv7|<{MyCE%oRzBN3TTn$ntxZVR3}^ZYLlNT8uo1h4}ffvNK;Io&Xhi% z>s`{*d4Cun1>-!N6_i=ei$&T$sXbzU!Z;OMWZob(Kj{ z&-ctX7;zQmrN)_2R9;@+k@mLG?>{n`%(V-KPUJWIdhT=Gv6IdDakscTil?jm&6g12 z3{b#Hjo+IBUW|UdWKOQ*EL{3V&N$AoojA*TG-6gcF`EYQxyqa_;Um1x$awj?Tu~h_ zOxU{jP3$>+kYn)2;x@$IA;(ICK;#G02KCynJYJ5u^w&8}V5bsO{UL4|eYjD=01wb5 z)r&G}n^Hc#)xB@^9fVamBN%sTNTQ)O)NBCe^O&57-9fW49Zge%Rq`=ErpbDxA%#n? z(ZGfw8?g8Y{wC90rFb;1#1WoqLR+~Mrs8hL#|A_J9$ZTt93vuC;oyb8p(B$zXA6|w zdqY}JiYbwl(%w5IMGV5OOm4(2hhxV-_IYS0At}_8fs&~tXMa2>C=))X9DG`m1?PecPQ=3^lBO#!ibahGNpNz9cKT3$Odg4>xz1}@Bn?5_Cb znkx%9lApHE`ZY|tvljD97|j&3UKkSq^NO!> znpdK6YTrBLG?K4U3%Y#pB%ykvxhM(%xmY+jq)57|r?=)@ky^|#DYFth66x^tMSV?M zRxJjGhGVRqWW>X#%GA*|R0&*^5iZGJNo)?T}n ziNimMnoFFVS>^cGx(Db-^0E$c@!trnzcZ9Xrk1~ zy2>f_!i45UI!=i|y08h)~w3L1f0-3rjA253r`OBBq-eV5u? z!V?kCOdY*O=>@u$Bw~NRE@q?p!#DIv#+px^yLZ)*W$y)zGSf6tUH|3YrHc|BwD=M6 zxr-8eO{*>#ECU-$<{Lh76%%z6aiMm{C9O~=>xNL>)b%gXt-1=6A6CY-GzLAJSoisg zumNYh$7l_V@{tCH8^p&c*<4)C-KLBpNBn&zLI_fmQh7K|m-1nlLDu$LrA5POTmbWV zX2Kmm=b?=f<4dG{ZspJF8kAX!xv}H>axXJ~=a)~; zroEs&C>8*Lr`hA$im@tq=)K${r$3pV-;j5XBa-mUCxTQIvlOK9g2LX?w^%Rh>3%XF zL~nXJBusM3s6x=}w(Q={!%W|p@g9IkTA-Zne}X}sNL{@^zXc zYv+GzNbXZwzm@?Q?o6Ngs=o2iwzBzzu5wCAcXqDY%6`W5g0+B!3mxo!3#wRPK ziBkI&YU;~>Z+I1adzAj@J~c668nXBtV)UB{AR~1IYj)7c>}a;eA1Xo6uk|c6Z?JR& zEq42oS{HZGV3(*6*fS^?nD9R2FHISEeM7(x_cX#lpK=J8$KpD*yL*xLlASf#^?(53 zId)>);I?KxqTJlKJv{N-!B8dYd}MLlOh8pp^z#G&Fx>h}+S}h%oM&Kce0S&%{G_{A z?e7oh@1H-p(CS{c-M!}HmRvD?v$Eyi0T73M@oa&7T~MhtO<%T}k#hRXb^qc^{|(ea zjtBhc*Sloj1LUhuqSF;lxwrN1`s5zjsT#gs<9l`1dj|XY1WR#IX&N`EpZbI68FcVa zyum3FToHfzLhHube(W>B$u2|e*tJ+(Y;p&dNm=DhGya(**@?#OXhT6dpr=p-jA}(i zf(@qSve_1~3VszTwi(|{(62kYhY!!EQ0mD?z39$&ZlSryOi|mhcZQb!w6v+RKIh@Y z0V-nA8?rxgi+}BX@;yezURM6CLen1frOFpw!+gFwO-NOnsqf4oUl{uzl6%^m+EdmK zTI!j$7i+U#@(szk#cG{kozd1|;*M))s`#pN~N|AL{*B9$J3pg6>O)i(M0OW@_)&j8=o1<#7oSy_%9 zO<5A)72(NGel)bySRXDo?Mo~;spD6WngBpzb&*hZ`<>{Hus?u+078%rT5cC>i$bp5 zP2AqKTb@9Fahw28^m?9R-mrORQHj@jMeqHQH!GzN7-W7FbCa*%Y6PwLj)IRgVZ@L0 zEAp-6KbtMds7~fgt?s{Hn4C0&!O81ulcxbwA`o)g+ta6hfmSNY0DhHv)SqWmW3#Ec zjX7p~4o4;Efpp%pxdF#);o(@Q9P;HiQlJbJ^;q~Is!Idd=LUVgGCd*%98 zIV1ij_=zso-Ico{>&fWA>ZCe5h!2-TEDgJfxxdQnH?Ie{4 zt%krF)3mXu1K@cOHcJ$p*w*I9ZlW%Cca_!Lo z=~>*Rt-etaN3dACI=bb3Hzkcnh`mN(fRHD#<0D=&hyN+-I|G{7qODH|O?v3k2`Z>m z5dmqICXF%R>Re*ra5rz$(|w zOI=;I5uNV)Tcfwn_$hv;JV(!UirRmbC5rx*Hxed3t9!O9X};QCCn0u0By1}xmltpI zAFU{yKCOT45PREcl)bqo?sKp1^%4pn04buSDWpCDH>md-TLW(_gk+;>A5pL&rZ}#b zY(Z_}d1;*T-p-9&dlvgTqd#!v>>@Gusq@Gkr@^!&h2vK9iq=&n=_J4I{+azY+v9YR z*9A))s4_Eps|j(Z3K#jPpoq7Fz3T2dHCKh_lF4Paz3ovr!!eQcaPb(pP|bdLQ3Svf*6oFg=!lc zn0VXer|!ca{%>%5!{or9?Bg83JB=aIl4ib?(-|AI@Q(Qm%X8=AG{;W{p_)tNY-U}b z^_+}OWn**IHz?rv0b0}Z)|s|qXUh|FMi{4MwxN2apKvK(m#^_g5rtN?f3b_Ix;Hg^)U$yTbmG0 zv}{m&(`u6AScsj7qx+JcdK1{ZY<)ZKK|!Y=^2Q)fU*Qi2C__*KfO8PmEt ziT7617cBY6Tv=H+my(1+{6?-cZmf?)Fay4RO(zs#NYh{l4*a%9TC#gWX6OT5WkSUX zz`A>(uB`EGV$C_fLeLG{Q+|QjFS-S9sP8>KV!HRmXC*1(bGX{`JL?Z_vwAlFOTY|V zv9~iN`IoFPc>QmAX9xOQ96gn~SMo+;%w_{WT0nkBmCnnQ*WZ05Il`syu_@|(FR9S8 zgMNTT9cOmMkyzH%snYUR%UvHVtc|)NzzVg$3l9UUd7#fak;qaJ(XBC6%si}3dJ*ed z!2g{TCZL(b1-0{lF&;~7hK(kzGWMV6S~F^!6x6Byw`O9mB}EWrxjh8$GI`Q~6Ff+i zQ2e&!iG(3TjUMcVIG<^yYX2)Np4izLl$e@L6ylR^D_HQvEhQcIm>PV@))1HCP`L)z zsMhAqhFW5Pz~@CKv>G}u@Z{xipG>B(GdUih=05&+v*I?Dk|3C{DN;vJeb+P?4{Qi} z|4Rn&kaa)m`grqe$O>O?r#Z__<%MNjmfYh`zpKu&>J=SgSEW zT~=VTZ_G_C?-NCMZ~s2t@T!#OApEcjzwy>tHnk>IGtT563dPZa6#gb-1YghNcof5X zQiWom!pBAgE-^s&&9-i4h^L7d?yQWdL4a&0k!;tW5qw|yR_IV3e25b2XKaJa7mMnJ zt!7SL2dfj%OuzniY5O1NFy6?GXzspCLF|&EN2rzoER7Ejkn4*4LOM~(_7sX72ZPKp zuFDRO-tIVZgTxlT4<$m$9US6tK-ETLW!@j`-A({U(eji(5=`&BY{tp}O^z6Xs9uEK z%S~2rz)$A7%3I?y+E~S;BR-O*@J%qkr>4hxUHGG1Nr{es&($41pG6DCj?l5RAcyxQ zk8(r*F3Z@Sef9UW-r;zRNY|=n#+KJ1f4)xDq*?kK6%xO&rhD<7(Xwxfwfs2;;wvvY ztZnNrk&0#tHhmL3_WghYDeNF zAbt5c9DM`rLhzsgS3Ih=<{9`|!i!#Q54n}0k?gx*B<`@KLkZ&M-fEGF;_)5+g%;Wy z8^%ve#N|qg=ZlMi9lnx#=WBg9{q#`44A&m$bQwuxkD zhTL;M`7-&St!QDr4YY*C-%^`Q2>?3=IY-t@#VlUsBQy&l|%P`V8dd!nNS#f!Et*;tw_-MPybrt=hg75DS85`+S{LX9!_=|Y{EkKS^9V^ZIcrIcK2*{7FnQduXRF9r&49H8E`ENjBne&J6L z)=I_{q}Dso%q-j_mX*h7eVjmK(z;RVY$E}dG6oB?jJ{oR8r~{(%kZI}_cg6}hDdr< z0x~Do4?@1(I5Xl9s<2~_2AQojWyUxzv|+sRejQdQtwIa&tA?Z)mRO=g?OuMy(MbJ0 zMAzLyA;<9^Bbw{~9bg51;vPtsJ$1QTeVEKiGJ#KyR3YA7sm&k#6Vr9qJOfo~dkQRk zn||{l4^zHA_KK8-T<>VDZWGSh$LD`$pl+Rl-m{kWqysO6#Ps{ebSrAmWiB6B9$s&4 ztNG9d;$)2;T&gp(ijHWu{FswXd~Biw>eqb(;JtJh3ec9+`3uk6!uzj}K84W-h}kUP zm~R6j?pek5>agrNKl-dHbQy9M=o#xoY37vE!Pw463axPA`uVr6lH zd*y=@!rA?m4q!*~@a`Lr#voYTR7wnZj)IEtdOkEoF6=9*5ks>&OPuUW7q+CY{%lw@7ux za>#~}KFy4mxgHMKmHM|=8zsP0QWh(p(L|QaCsVnspRJ|@hrf6FR@Z09y$X&IMor!d zSzt#mJXT#c%R?!E@wM^O%D(STGyi-&d|<9-#iLyrW)qzTMFw)D$98fwr=E;J0N|S| zJlqZwiRCk$D97&`ueqc4tRZgp>ymRUAC8SmVz}AZ@d+Z%u$~_Hiak@2b5Sq)Q{2m(s#af4_~T2vu5}FJ|BRJJU-gBDRyQ z>FF^M16qssdHxQ<0DKQARvWnHBSzKh7@(Z$e{lSDZk$L*>|nev?gBDT`&>1t!$9jm zyz44FrP>4h+~_$7sS##e&F86~E3=Dw%)CjSInz)gT)u5IRV)Kssa=Mt*_#HMrDAXO*&0V%hzjVXAVfXM zJMJMozujg(t~1Qzn_W4c^YVO@#QDk1P$WjLp>>XV{Jhy*p>)RokQj-Q6u4F`kD&r> z7oP)Nd-~lzVzOkf|4ja+=4PlQ4b|OlcJc6Ql_~XFGcOvtnvrqK;*4mPwjz+d*_{C9 zp8!*m@u1>{a{t`x9eH)SyP+UNT8#;-rU-t&!Ryi6j_vHCtIp=cO%*&a8xG)u_qMiD zlRB1Bp&zc<%qf3A-Z zqTWXOL5NHLjYD#iAf!2Mw{U{X7@v)Y4gxS z#8j*c1Z@s#3A}norfzG0*rr*aPHT}@T>`xl2;v6WP>*wms2GcxpL~&KN(Emx-0>}n zj^sO9vnztxn>{5Ydp!W#ni4Gw-82=;Y{39I*E+$B`8}XSCgMlpN$$gnH>1itY_4Jc zR`ZJ;3UbFEMLHJsNmD%p1QXVRIA7iszPlE@`;`>f_s#1|?<=<_u5!^lyzcKXMW|%! zDH2y|{pvJ*Q~=FKC!;ncaAc%65|H`egKF-N74^9s^F{Q$VFh%T8B$r$;%1aI;qyhWt%aO$JlLlIHT zEl4mNq1|a9IRFEN_O+SwjQYu%l)GX*kA22B)%RzE7?I}2|A@nT+gs@Y!kYf+T5yo3 zN&FDDHj^8mZ6q8SaqD63B1qAiBBA8g6B`HLlwN=FCeQgc|F$m!l4u<8Lt zDtT$dsw+l2EbG}1F;Ihntj1=$9s*MX-L;z}`sR=*V>GN<<_Cd+{_zd1#D1bP=%`yD z&6s=dE(wA3l49m3e+oLi%O-^YNT2%^-?+x>aLsnOn=mSy40pMGV3&bs1fzWc(S3{( zvQ(WNu$b8wsUJ~p56wtU8Axq06k0lojZ4ugl#*~CSuSwd4sUH1>gI($Ng**kwxe2d zf019S4Bv7(^l27EpR+8rwK9@Cj2A62+9#f>+$-%DIoFh6ewqE@by45f#&*UAk%pso zll5L*vF4`LVvgh8BT2CuCc?kio1gwrVTWy<(lY5d_neX<8$T%BQg98QAAH_*T=tTk zI(HeGieFsTCPiGb)%j2s^|Zr-&)N=huX471_G!SEZaNhp&oKdXavZuVrw0Vf&l2&zE~M zQ^~TBP3({0>;Rw6n%C!JQT0g+C}YJID(JYWOt9o6dWPZ4UWKNZV(HfeWyaUsNW=bP zt=n#qjS;1~>)hZX+01i_7V|C0aNK?^w?HLbcY{cXVOth^5q9EZZ)I?HR?zT{&Sv)K z@KAKg-o?S>jeYgRq3~|zK23FmyzLcAKxT0n8GG9M?{4nh^>&Xh^jkW8ueV!x{_Pq{ z=9F9cWT`O%i#>0fV7;Xo`1bin{hmNDpO}o?uan?0vNIwaB2oT7irtxYoVldj!3hdQ z<|>cWHaE<|2p$G`K0w=yR|621`!(CP;oWD)wdHikZFE-|3|ph+SYNvd-@EIud}o)L$;Ra9NV2` zmDVcThDUwpCo})3m$~c}LSoG%*kagk4rw5o-XKVwZ3W<7WZrUZn*-2_V=s+5*<- zJ%X{}t`xEf{?(=!AvA{Cf&k?20Znm8rS0C_8h)mV-3;N@Z9 z3^sFEeIn{@owF1bl!dt-F_gq9DRTh>=9XBk+tpF4qz=4@MGDaIL_*<}WAMf=mNJZF zp%%PoC(pLwech<4T8&s!9$hp=~ZT#+0C0ann=DefvAvsP-=^uw1i|fdZ7^TqG|a;!Sn$ zA9F(oxh#@-r5fwS{)^cPV5~Th&rr10wXh^UGwSXn0kwI@9G_Jn>uGJh)U|6xn=0Xd z@rkTMW29p)O6}PRCMoPLlg_r4Q0!yGFR~-tLA6_lM!I4_dFBkRSFN(P_^Kt(Ir?Ni z0)Jner60P1$?3uJ8O_BObp}1C>e`(&WP#>)&H+~`?jX2&7eFHrd|)Typt{ILxodPq zljdkP>DayQJ&XMxY>ICne!JX`@_l#@{A1|`YN#zJU`ItPCX!At{=}qu@vJ9!oh;!v ztVpU*G**|5YQ zm{pDr2X}BPc0JwRDu4AZ#h@>X|FU$lC$bpM!r>FIi}?`N{Em8r2iKP#+uNYutSudu zJ4XZrya|wtm0i}SaBd>ju#go*O4Q!g5Tu{`>vbAKPV^PMKTTCWP5<`Vm1lw4YI1$j zTh9ROVQP<;E0|*1B*7$Ao*qC)faoU@9XKpu@`pyf&24HXFqiavt?5l*)X?pA4f%VJ zMt2fE4$U3tK46m^Tud3hEYt{7UN~4h?nwZtg7C{5p#pHfR}ZWC)r+dY1-*r(@9_X` zE8L7YJ*bwyAzjwLa&&qb3nlUL@}hX$RpYx>JvYr}U}n{MH*;8IDbRD88S@?9%SayL z2JlZ#Ib#}bmjs+-d1F^V1%xxj>|#_l1uokfe=Hiv04Yy3U2FQ&c*Fb&k+mZ3zSP4% z^^D0mvaM6B^jB|X{8)>AZhvmTWBAJXOcwf50IqP$lpkKrafk0=Zd`Jt9+P&gSzzDZ z@@ieh?bofsKy84&5&xZw$WajQ_TJ)sTeVPPP~ zR!LNh>O`Hb4U2FA^@Zn&H_?E~c?*Nm!*)n<*T~TZwsB1^spebU260*MD1_8h$Fv*7T*590ZKhR@Zx5kGQx}Zsh8#y{am@Q^ zL~$S-)}8D)7J=FfgP`wgs}E3AvflIYJwA7swn`8!8d}d+HYis%Un!n&LP_o5N>-|y?H^HR=J z0IQL{1jn^#5}qC8I2!^>CIT)sBL3GfzJqpJ5t-0k(^>I2l{T zS3k9RUaYyB-OlMh&wG=%T@`xV;hQPQsioXfbaNLyO_=QPB6rXBx|a43X`ZHyZSmZ; z!wHxVQUU+8wSX`+DoVhGcNRFX1qq4#ey-2_&QgXZKGf8`b0pn|)MDg*f7p-mP?m@p zl8b8E@;SpaY7sN0xEO{QF;>f_gqFi^iw(RET=d%ML*Y}$i<1U4kKkwpb(&RH= zy=y&xr8d=CBvi&p2aD%b>a0H5{;q-lg1D1_qaQD&>A?25uT!RM+Jd8W)J#m2FHGmc z5?H|U#wfrxL_5)}T_IIFp}kV*VC;Q#YBEQq%6KSWl5{6d(Y|)h$2xD$=eu1;P5C6& z)q0nOG)!4q)uL)kPG|A#D{RzzK5SVhpAg4u96zdJKy?3XJI=^F%MWetE{l{1?~a%% zavhVg-2f7k+TO{*|E&sSqe#GjMj zEfgP^5O>VOP_aL+z>xizS?rb26jNkLz6HEViBrRQE3)`x+uKdURcvlt+C$@m@00c%Qtq(8 zdN4y2FdKd-l5RDPW9MP+GUY)FCgL)UZ2K&0)7tE^9XzRFix*GRhzOMGm_D7&xzQH# zw3-gmXoR>(4K0QZQ-1>jA#u9W5Jl>F*L^DoOm?SidIe+0)X&(Onz1+DezBGM}L-+I7~6D;Sbhx5Sz=bCO(o!P(iypNT%md{Xj!Rlzuom{i-mOqE;#=- zjr&XVYm$oLI_suehASKn$v3(K&e+lPaOPguaZ;HgX_lANizvVa*}V3?)e{QE)_SGD zEz&bhQ)P?57Dh0qHF_@X_OGDM+=VZn2@o_D5NC^Flr|&{DoqLGA6(Zbn>PUBP|3`e zMj`t`jEY^@?~_0cO~>aN=lYa0d*8J5eLFn7^SW?cw?DYip~ci@@v65>l;GJAPO!+C zH>Q`%6)sKX*D9Q@=T$z39PlE6ylMC7#$4#Ast@7612sq^Kl&rWTJ^Pq zDx%hl4!G&L-1w6M8KWm0m8Um0d-!M^l-*cRs0Gwc}%gv<|_Op}tQa0rtApq%~445!s&G4$jL8@tP9!S2OM*~%!6-&GZ3=KxcWDt=Qo?+BVs?4 zRSl%+5NLRE|I)jSYMh^V>WlTF8?LyGmJyU*@`~T1E1$F0VK91HDB#vrkB2t$pyNUD zA)z%ftI}G->kP-=(3#-86^-#(X?=Q~lz(jY{3wsG{p|CXf~Nf>RDgvv5(AkLf`92B z`-Di?X#&fVBx90|_H>s6%Ojlzhv~KREs@TN& zndzgOG9H^lycy6Hj~4O&dl3~%;Pc?R2OQ}FJ>mgF?djYkd1|44`};nAHf*KAQ}|ci zqsJx_Xjsnq;LuYMx3oK1-F2DuKbNL95En`h03XGUFwAG-go_zw7y-GLuA{C`*2sy@ z1NjU~UVrmwiE%2HzIqQ~GY4ueyia5D{7+IQGFnIRf8=Vg%2k7tI)8ifQ}4(T67|;T zrYPsio5MR)bHY-m*BtBl0VpIdSA zR{n!ndz!SaaaOq%eJYb2XgTz_R2F!Xp#$K1>5s;*z#@F-Wct(BukHl*2^NaIRk#-K%`@5?5dFr+ ztn0un!(F{-LaMyG=3~6CUmY7-#V_bLJ8UpUAiK2_SiD!~V_cWcluO56?gR>eyxh5Wbe0Y+)1+RG6 z+opMaza?)57w4Aoy!TA=GS*AsTJ&CTBfKH!#=Ki2Lw*twL5}7IW?)BAmZX!#yoaEv zNf{5BE3organisation->department->scope + * manage opening hours including closed days + * login-system with different access levels + * pickup for documents + * ticketprinter support for customers without appointments (authenticated, lockable, timeable) + * calldisplay support + * collecting statistics like waiting time or served clients per day + * emergency call for employees + + This documentation covers the Citizen Frontend Bürgeransicht API-level access to these features. + termsOfService: 'http://service.berlin.de/terminvereinbarung/' + contact: + name: Mathias Fischer + email: mathias.fischer@berlinonline.de + url: 'http://www.berlinonline.net/unternehmen/team/' + license: + name: Commercial + url: 'http://www.berlinonline.net' \ No newline at end of file diff --git a/zmscitizenapi/public/doc/partials/tags.yaml b/zmscitizenapi/public/doc/partials/tags.yaml new file mode 100644 index 000000000..e2944a00f --- /dev/null +++ b/zmscitizenapi/public/doc/partials/tags.yaml @@ -0,0 +1,6 @@ +tags: + - name: zmscitizenapi + description: | + The zmscitizenapi is a streamlined API designed specifically for the citizen-facing frontend application "Bürgeransicht." It acts as an intermediary between the Bürgeransicht frontend and various backend services, including the broader zmsapi system. The zmscitizenapi consolidates and simplifies endpoints from zmsapi and other integrated services to provide a cohesive experience for users, enabling actions such as appointment booking, service requests, and other interactions with municipal services. Its purpose is to enhance the usability of the citizen interface by providing a lightweight and efficient layer on top of the complex zmsapi structure. + + diff --git a/zmscitizenapi/public/doc/partials/version.yaml b/zmscitizenapi/public/doc/partials/version.yaml new file mode 100644 index 000000000..5b308b04f --- /dev/null +++ b/zmscitizenapi/public/doc/partials/version.yaml @@ -0,0 +1 @@ +swagger: "2.0" \ No newline at end of file diff --git a/zmscitizenapi/public/doc/schema b/zmscitizenapi/public/doc/schema new file mode 120000 index 000000000..c83e4ce91 --- /dev/null +++ b/zmscitizenapi/public/doc/schema @@ -0,0 +1 @@ +../../vendor/eappointment/zmsentities/schema \ No newline at end of file diff --git a/zmscitizenapi/public/index.php b/zmscitizenapi/public/index.php new file mode 100644 index 000000000..1c83fad51 --- /dev/null +++ b/zmscitizenapi/public/index.php @@ -0,0 +1,4 @@ +run(); diff --git a/zmscitizenapi/routing.php b/zmscitizenapi/routing.php new file mode 100644 index 000000000..5060cb8e6 --- /dev/null +++ b/zmscitizenapi/routing.php @@ -0,0 +1,602 @@ +get( + '/services/', + '\BO\Zmscitizenapi\ServicesList' +) + ->setName("ServicesList"); + +/** + * @swagger + * /scopes/: + * get: + * summary: Get the list of scopes + * tags: + * - scopes + * responses: + * 200: + * description: List of scopes + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/scopes.json" + */ +\App::$slim->get( + '/scopes/', + '\BO\Zmscitizenapi\ScopesList' +) + ->setName("ScopesList"); + +/** + * @swagger + * /offices/: + * get: + * summary: Get the list of offices + * tags: + * - offices + * responses: + * 200: + * description: List of offices + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/offices.json" + */ +\App::$slim->get( + '/offices/', + '\BO\Zmscitizenapi\OfficesList' +) + ->setName("OfficesList"); + +/** + * @swagger + * /offices-and-services/: + * get: + * summary: Get the relations between offices and services + * tags: + * - offices-services + * responses: + * 200: + * description: List of office-service relations + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/offices-and-services.json" + */ +\App::$slim->get( + '/offices-and-services/', + '\BO\Zmscitizenapi\OfficesServicesRelations' +) + ->setName("OfficesServicesRelations"); + +/** + * @swagger + * /scope-by-id/: + * get: + * summary: Get a scope by ID + * tags: + * - scopes + * parameters: + * - name: id + * description: Scope ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: Scope details + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/scope.json" + * 404: + * description: Scope not found + */ +\App::$slim->get( + '/scope-by-id/', + '\BO\Zmscitizenapi\ScopeByIdGet' +) + ->setName("ScopeByIdGet"); + +/** + * @swagger + * /services-by-office/: + * get: + * summary: Get the services offered by a specific office + * tags: + * - services + * parameters: + * - name: officeId + * description: Office ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of services for the office + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/services-by-office.json" + */ +\App::$slim->get( + '/services-by-office/', + '\BO\Zmscitizenapi\ServicesByOfficeList' +) + ->setName("ServicesByOfficeList"); + +/** + * @swagger + * /offices-by-service/: + * get: + * summary: Get the offices that offer a specific service + * tags: + * - offices + * parameters: + * - name: serviceId + * description: Service ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of offices offering the service + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/offices-by-service.json" + */ +\App::$slim->get( + '/offices-by-service/', + '\BO\Zmscitizenapi\OfficesByServiceList' +) + ->setName("OfficesByServiceList"); + +/** + * @swagger + * /available-days/: + * get: + * summary: Get the list of available days for appointments + * tags: + * - appointments + * parameters: + * - name: officeId + * description: Office ID + * in: query + * required: true + * type: integer + * - name: serviceId + * description: Service ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of available days + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/available-days.json" + */ +\App::$slim->get( + '/available-days/', + '\BO\Zmscitizenapi\AvailableDaysList' +) + ->setName("AvailableDaysList"); + +/** + * @swagger + * /available-appointments/: + * get: + * summary: Get available appointments for a specific day + * tags: + * - appointments + * parameters: + * - name: date + * description: Date in format YYYY-MM-DD + * in: query + * required: true + * type: string + * - name: officeId + * description: Office ID + * in: query + * required: true + * type: integer + * - name: serviceId + * description: Service ID + * in: query + * required: true + * type: integer + * responses: + * 200: + * description: List of available appointments + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/available-appointments.json" + */ +\App::$slim->get( + '/available-appointments/', + '\BO\Zmscitizenapi\AvailableAppointmentsList' +) + ->setName("AvailableAppointmentsList"); + +/** + * @swagger + * /appointment/: + * get: + * summary: Get an appointment by process ID + * tags: + * - appointments + * parameters: + * - name: processId + * description: The unique identifier for the process. Must be an integer starting with 10 or 11, e.g., 100348. + * in: query + * required: true + * type: integer + * - name: authKey + * description: The authentication key consisting of 4 to 5 alphanumeric characters, e.g., 42a3. + * in: query + * required: true + * type: string + * responses: + * 200: + * description: Appointment details + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->get( + '/appointment/', + '\BO\Zmscitizenapi\AppointmentGet' +) + ->setName("AppointmentGet"); + +/** + * @swagger + * /captcha-details/: + * get: + * summary: Get CAPTCHA details + * tags: + * - captcha + * responses: + * 200: + * description: CAPTCHA details + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/captcha-details.json" + */ +\App::$slim->get( + '/captcha-details/', + '\BO\Zmscitizenapi\CaptchaGet' +) + ->setName("CaptchaGet"); + +/** + * @swagger + * /reserve-appointment/: + * post: + * summary: Reserve an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment reservation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointment-reserve.json" + * responses: + * 200: + * description: Reservation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/reserve-appointment/', + '\BO\Zmscitizenapi\AppointmentReserve' +) + ->setName("AppointmentReserve"); + +/** + * @swagger + * /update-appointment/: + * post: + * summary: Update an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment update data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointment-update.json" + * responses: + * 200: + * description: Update successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/update-appointment/', + '\BO\Zmscitizenapi\AppointmentUpdate' +) + ->setName("AppointmentUpdate"); + +/** + * @swagger + * /confirm-appointment/: + * post: + * summary: Confirm an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment confirmation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointment-confirm.json" + * responses: + * 200: + * description: Confirmation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/confirm-appointment/', + '\BO\Zmscitizenapi\AppointmentConfirm' +) + ->setName("AppointmentConfirm"); + +/** + * @swagger + * /preconfirm-appointment/: + * post: + * summary: Preconfirm an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment preconfirmation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointment-preconfirm.json" + * responses: + * 200: + * description: Preconfirmation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/preconfirm-appointment/', + '\BO\Zmscitizenapi\AppointmentPreconfirm' +) + ->setName("AppointmentPreconfirm"); + +/** + * @swagger + * /cancel-appointment/: + * post: + * summary: Cancel an appointment + * tags: + * - appointments + * parameters: + * - name: appointment + * description: Appointment cancellation data + * in: body + * required: true + * schema: + * $ref: "schema/citizenapi/appointment-cancel.json" + * responses: + * 200: + * description: Cancellation successful + * schema: + * type: object + * properties: + * meta: + * $ref: "schema/metaresult.json" + * data: + * $ref: "schema/citizenapi/appointment.json" + * 400: + * description: Invalid input + * schema: + * type: object + * properties: + * errors: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * msg: + * type: string + * path: + * type: string + * location: + * type: string + * 404: + * description: Appointment not found + */ +\App::$slim->post( + '/cancel-appointment/', + '\BO\Zmscitizenapi\AppointmentCancel' +) + ->setName("AppointmentCancel"); diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php new file mode 100644 index 000000000..277927929 --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php @@ -0,0 +1,16 @@ +handle($request); + } +} \ No newline at end of file diff --git a/zmscitizenapi/templates/.keep b/zmscitizenapi/templates/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php new file mode 100644 index 000000000..a38e2e30b --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentCancelTest.php @@ -0,0 +1,14 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php new file mode 100644 index 000000000..04d3d9e25 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentConfirmTest.php @@ -0,0 +1,13 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php new file mode 100644 index 000000000..3208be31f --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentPreconfirmTest.php @@ -0,0 +1,14 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php new file mode 100644 index 000000000..48279fd9f --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -0,0 +1,14 @@ +renderJson(method: 'POST'); + $this->assertEqualsCanonicalizing([], $responseData); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/Base.php b/zmscitizenapi/tests/Zmscitizenapi/Base.php new file mode 100644 index 000000000..d06e1a796 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/Base.php @@ -0,0 +1,29 @@ +render($arguments, $parameters, $sessionData, $method); + $this->assertContains($response->getStatusCode(), $assertStatusCodes); + return json_decode($response->getBody(), true); + } +} diff --git a/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php b/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php new file mode 100644 index 000000000..e5639226d --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php @@ -0,0 +1,11 @@ + Date: Fri, 27 Sep 2024 18:13:33 +0200 Subject: [PATCH 12/42] feat(MPDZBS-877): Readd files lost in github history --- zmsentities/src/Zmsentities/Scope.php | 35 ++++++++++++++++++++++++++ zmsentities/src/Zmsentities/Source.php | 12 +++++++++ 2 files changed, 47 insertions(+) diff --git a/zmsentities/src/Zmsentities/Scope.php b/zmsentities/src/Zmsentities/Scope.php index 3238f9769..1919c6094 100644 --- a/zmsentities/src/Zmsentities/Scope.php +++ b/zmsentities/src/Zmsentities/Scope.php @@ -25,6 +25,41 @@ public function getSource() return $this->toProperty()->source->get(); } + public function getTelephoneActivated() + { + return $this->getPreference('client', 'telephoneActivated', null); + } + + public function getTelephoneRequired() + { + return $this->getPreference('client', 'telephoneRequired', null); + } + + public function getCustomTextfieldActivated() + { + return $this->getPreference('client', 'customTextfieldActivated', null); + } + + public function getCustomTextfieldRequired() + { + return $this->getPreference('client', 'customTextfieldRequired', null); + } + + public function getCustomTextfieldLabel() + { + return $this->getPreference('client', 'customTextfieldLabel', ''); + } + + public function getCaptchaActivatedRequired() + { + return $this->getPreference('client', 'captchaActivatedRequired', null); + } + + public function getDisplayInfo() + { + return $this->getPreference('appointment', 'infoForAppointment', null); + } + public function getProvider() { if (!$this->provider instanceof Provider) { diff --git a/zmsentities/src/Zmsentities/Source.php b/zmsentities/src/Zmsentities/Source.php index b0cfaa397..fe6a8fb30 100644 --- a/zmsentities/src/Zmsentities/Source.php +++ b/zmsentities/src/Zmsentities/Source.php @@ -51,6 +51,18 @@ public function getProviderList() return $providerList; } + public function getScopeList() + { + $scopeList = new Collection\ScopeList(); + foreach ($this->toProperty()->scopes->get() as $scope) { + if (!$scope instanceof Scope) { + $scope = new Scope($scope); + } + $scopeList->addEntity($scope); + } + return $scopeList; + } + public function getRequestList() { $requestList = new Collection\RequestList(); From ef0babdd14929b451a04073797d77588aaed2d78 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Fri, 27 Sep 2024 18:14:26 +0200 Subject: [PATCH 13/42] feat(MPDZBS-877): Resolve merge conflict in zmsentities --- zmsentities/package-lock.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zmsentities/package-lock.json b/zmsentities/package-lock.json index e979ac461..2e03b7fa5 100644 --- a/zmsentities/package-lock.json +++ b/zmsentities/package-lock.json @@ -1,10 +1,6 @@ { "name": "eappointment-zmsentities", -<<<<<<< HEAD - "version": "file:../../../../zmsentities", -======= "version": "1.0.0", ->>>>>>> next "lockfileVersion": 3, "requires": true, "packages": { From a1073e22085d146f143785121f5567f9c6b6f017 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Fri, 27 Sep 2024 18:27:53 +0200 Subject: [PATCH 14/42] feat(MPDZBS-877): Readd zmscitizenapi schemas to zmsentities deleted by github commit history --- .../schema/citizenapi/appointment-cancel.json | 10 + .../citizenapi/appointment-confirm.json | 10 + .../citizenapi/appointment-preconfirm.json | 10 + .../citizenapi/appointment-reserve.json | 10 + .../schema/citizenapi/appointment-update.json | 10 + .../schema/citizenapi/appointment.json | 505 ++++++++++++++++++ .../citizenapi/available-appointments.json | 19 + .../schema/citizenapi/available-days.json | 19 + .../schema/citizenapi/captcha-details.json | 24 + .../citizenapi/offices-and-services.json | 154 ++++++ .../schema/citizenapi/offices-by-service.json | 27 + zmsentities/schema/citizenapi/offices.json | 82 +++ zmsentities/schema/citizenapi/scope.json | 68 +++ zmsentities/schema/citizenapi/scopes.json | 67 +++ .../schema/citizenapi/services-by-office.json | 31 ++ zmsentities/schema/citizenapi/services.json | 31 ++ 16 files changed, 1077 insertions(+) create mode 100644 zmsentities/schema/citizenapi/appointment-cancel.json create mode 100644 zmsentities/schema/citizenapi/appointment-confirm.json create mode 100644 zmsentities/schema/citizenapi/appointment-preconfirm.json create mode 100644 zmsentities/schema/citizenapi/appointment-reserve.json create mode 100644 zmsentities/schema/citizenapi/appointment-update.json create mode 100644 zmsentities/schema/citizenapi/appointment.json create mode 100644 zmsentities/schema/citizenapi/available-appointments.json create mode 100644 zmsentities/schema/citizenapi/available-days.json create mode 100644 zmsentities/schema/citizenapi/captcha-details.json create mode 100644 zmsentities/schema/citizenapi/offices-and-services.json create mode 100644 zmsentities/schema/citizenapi/offices-by-service.json create mode 100644 zmsentities/schema/citizenapi/offices.json create mode 100644 zmsentities/schema/citizenapi/scope.json create mode 100644 zmsentities/schema/citizenapi/scopes.json create mode 100644 zmsentities/schema/citizenapi/services-by-office.json create mode 100644 zmsentities/schema/citizenapi/services.json diff --git a/zmsentities/schema/citizenapi/appointment-cancel.json b/zmsentities/schema/citizenapi/appointment-cancel.json new file mode 100644 index 000000000..449e115ce --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment-cancel.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Includes a message indicating the result of a canceled appointment request.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointment-confirm.json b/zmsentities/schema/citizenapi/appointment-confirm.json new file mode 100644 index 000000000..f77648b3a --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment-confirm.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Contains a message confirming the successful confirmation of an appointment.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointment-preconfirm.json b/zmsentities/schema/citizenapi/appointment-preconfirm.json new file mode 100644 index 000000000..e2537a038 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment-preconfirm.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Provides a message indicating the successful pre-confirmation of an appointment.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointment-reserve.json b/zmsentities/schema/citizenapi/appointment-reserve.json new file mode 100644 index 000000000..c3d1faebc --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment-reserve.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Includes a message confirming that an appointment has been successfully reserved.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointment-update.json b/zmsentities/schema/citizenapi/appointment-update.json new file mode 100644 index 000000000..2a68fad92 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment-update.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "description": "Provides a message indicating the result of an appointment update operation.", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["message"] +} diff --git a/zmsentities/schema/citizenapi/appointment.json b/zmsentities/schema/citizenapi/appointment.json new file mode 100644 index 000000000..14c081b06 --- /dev/null +++ b/zmsentities/schema/citizenapi/appointment.json @@ -0,0 +1,505 @@ +{ + "type": "object", + "description": "Includes a message that indicates the result or status of an appointment-related operation.", + "properties": { + "processId": { + "type": "string" + }, + "timestamp": { + "type": "number" + }, + "authKey": { + "type": "string" + }, + "familyName": { + "type": "string" + }, + "customTextfield": { + "type": "string" + }, + "email": { + "type": "string" + }, + "telephone": { + "type": "string" + }, + "officeName": { + "type": "string" + }, + "officeId": { + "type": "string" + }, + "scope": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + }, + "contact": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "street": { + "type": "string" + }, + "email": { + "type": "string" + }, + "country": { + "type": "string" + } + }, + "required": [ + "name", + "street", + "email", + "country" + ] + }, + "provider": { + "type": "object", + "properties": { + "contact": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "name": { + "type": "string" + }, + "postalCode": { + "type": "string" + }, + "region": { + "type": "string" + }, + "street": { + "type": "string" + }, + "streetNumber": { + "type": "string" + } + }, + "required": [ + "city", + "country", + "name", + "postalCode", + "region", + "street", + "streetNumber" + ] + }, + "id": { + "type": "string" + }, + "link": { + "type": "string" + }, + "name": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "contact", + "id", + "link", + "name", + "displayName", + "source" + ] + }, + "hint": { + "type": "string" + }, + "lastChange": { + "type": "number" + }, + "preferences": { + "type": "object", + "properties": { + "appointment": { + "type": "object", + "properties": { + "deallocationDuration": { + "type": "string" + }, + "infoForAppointment": { + "type": "string" + }, + "endInDaysDefault": { + "type": "string" + }, + "multipleSlotsEnabled": { + "type": "string" + }, + "reservationDuration": { + "type": "string" + }, + "activationDuration": { + "type": "string" + }, + "startInDaysDefault": { + "type": "string" + }, + "notificationConfirmationEnabled": { + "type": "string" + }, + "notificationHeadsUpEnabled": { + "type": "string" + } + }, + "required": [ + "deallocationDuration", + "infoForAppointment", + "endInDaysDefault", + "multipleSlotsEnabled", + "reservationDuration", + "activationDuration", + "startInDaysDefault", + "notificationConfirmationEnabled", + "notificationHeadsUpEnabled" + ] + }, + "client": { + "type": "object", + "properties": { + "alternateAppointmentUrl": { + "type": "string" + }, + "amendmentActivated": { + "type": "string" + }, + "amendmentLabel": { + "type": "string" + }, + "emailFrom": { + "type": "string" + }, + "emailRequired": { + "type": "string" + }, + "emailConfirmationActivated": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "appointmentsPerMail": { + "type": "string" + }, + "slotsPerAppointment": { + "type": "string" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "string" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + }, + "adminMailOnAppointment": { + "type": "number" + }, + "adminMailOnDeleted": { + "type": "number" + }, + "adminMailOnUpdated": { + "type": "number" + }, + "adminMailOnMailSent": { + "type": "number" + } + }, + "required": [ + "alternateAppointmentUrl", + "amendmentActivated", + "amendmentLabel", + "emailFrom", + "emailRequired", + "emailConfirmationActivated", + "telephoneActivated", + "telephoneRequired", + "appointmentsPerMail", + "slotsPerAppointment", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired", + "adminMailOnAppointment", + "adminMailOnDeleted", + "adminMailOnUpdated", + "adminMailOnMailSent" + ] + }, + "notifications": { + "type": "object", + "properties": { + "confirmationContent": { + "type": "string" + }, + "headsUpContent": { + "type": "string" + }, + "headsUpTime": { + "type": "string" + } + }, + "required": [ + "confirmationContent", + "headsUpContent", + "headsUpTime" + ] + }, + "pickup": { + "type": "object", + "properties": { + "alternateName": { + "type": "string" + }, + "isDefault": { + "type": "string" + } + }, + "required": [ + "alternateName", + "isDefault" + ] + }, + "queue": { + "type": "object", + "properties": { + "callCountMax": { + "type": "string" + }, + "callDisplayText": { + "type": "string" + }, + "firstNumber": { + "type": "string" + }, + "lastNumber": { + "type": "string" + }, + "maxNumberContingent": { + "type": "string" + }, + "processingTimeAverage": { + "type": "string" + }, + "publishWaitingTimeEnabled": { + "type": "string" + }, + "statisticsEnabled": { + "type": "string" + } + }, + "required": [ + "callCountMax", + "callDisplayText", + "firstNumber", + "lastNumber", + "maxNumberContingent", + "processingTimeAverage", + "publishWaitingTimeEnabled", + "statisticsEnabled" + ] + }, + "survey": { + "type": "object", + "properties": { + "emailContent": { + "type": "string" + }, + "enabled": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "emailContent", + "enabled", + "label" + ] + }, + "ticketprinter": { + "type": "object", + "properties": { + "buttonName": { + "type": "string" + }, + "confirmationEnabled": { + "type": "string" + }, + "deactivatedText": { + "type": "string" + }, + "notificationsAmendmentEnabled": { + "type": "string" + }, + "notificationsEnabled": { + "type": "string" + }, + "notificationsDelay": { + "type": "string" + } + }, + "required": [ + "buttonName", + "confirmationEnabled", + "deactivatedText", + "notificationsAmendmentEnabled", + "notificationsEnabled", + "notificationsDelay" + ] + }, + "workstation": { + "type": "object", + "properties": { + "emergencyEnabled": { + "type": "string" + }, + "emergencyRefreshInterval": { + "type": "string" + } + }, + "required": [ + "emergencyEnabled", + "emergencyRefreshInterval" + ] + } + }, + "required": [ + "appointment", + "client", + "notifications", + "pickup", + "queue", + "survey", + "ticketprinter", + "workstation" + ] + }, + "shortName": { + "type": "string" + }, + "status": { + "type": "object", + "properties": { + "emergency": { + "type": "object", + "properties": { + "activated": { + "type": "string" + } + }, + "required": [ + "activated" + ] + }, + "queue": { + "type": "object", + "properties": { + "ghostWorkstationCount": { + "type": "string" + }, + "givenNumberCount": { + "type": "string" + }, + "lastGivenNumber": { + "type": "string" + }, + "lastGivenNumberTimestamp": { + "type": "number" + } + }, + "required": [ + "ghostWorkstationCount", + "givenNumberCount", + "lastGivenNumber", + "lastGivenNumberTimestamp" + ] + }, + "ticketprinter": { + "type": "object", + "properties": { + "deactivated": { + "type": "string" + } + }, + "required": [ + "deactivated" + ] + } + }, + "required": [ + "emergency", + "queue", + "ticketprinter" + ] + } + }, + "required": [ + "id", + "source", + "contact", + "provider", + "hint", + "lastChange", + "preferences", + "shortName", + "status" + ] + }, + "subRequestCounts": { + "type": "array", + "items": {} + }, + "serviceId": { + "type": "string" + }, + "serviceCount": { + "type": "number" + } + }, + "required": [ + "processId", + "timestamp", + "authKey", + "familyName", + "customTextfield", + "email", + "telephone", + "officeName", + "officeId", + "scope", + "subRequestCounts", + "serviceId", + "serviceCount" + ] +} \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/available-appointments.json b/zmsentities/schema/citizenapi/available-appointments.json new file mode 100644 index 000000000..d3c5fdecf --- /dev/null +++ b/zmsentities/schema/citizenapi/available-appointments.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "description": "Lists available appointment timestamps and the last modification time for the appointment data.", + "properties": { + "appointmentTimestamps": { + "type": "array", + "items": { + "type": "number" + } + }, + "lastModified": { + "type": "number" + } + }, + "required": [ + "appointmentTimestamps", + "lastModified" + ] +} diff --git a/zmsentities/schema/citizenapi/available-days.json b/zmsentities/schema/citizenapi/available-days.json new file mode 100644 index 000000000..5a030908c --- /dev/null +++ b/zmsentities/schema/citizenapi/available-days.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "description": "Contains available days for scheduling appointments and the last modification time for this data.", + "properties": { + "availableDays": { + "type": "array", + "items": { + "type": "string" + } + }, + "lastModified": { + "type": "number" + } + }, + "required": [ + "availableDays", + "lastModified" + ] +} diff --git a/zmsentities/schema/citizenapi/captcha-details.json b/zmsentities/schema/citizenapi/captcha-details.json new file mode 100644 index 000000000..30b7ac2d5 --- /dev/null +++ b/zmsentities/schema/citizenapi/captcha-details.json @@ -0,0 +1,24 @@ +{ + "type": "object", + "description": "Provides CAPTCHA configuration details including site key, endpoint, puzzle status, and CAPTCHA enablement.", + "properties": { + "siteKey": { + "type": "string" + }, + "captchaEndpoint": { + "type": "string" + }, + "puzzle": { + "type": "boolean" + }, + "captchaEnabled": { + "type": "boolean" + } + }, + "required": [ + "siteKey", + "captchaEndpoint", + "puzzle", + "captchaEnabled" + ] +} diff --git a/zmsentities/schema/citizenapi/offices-and-services.json b/zmsentities/schema/citizenapi/offices-and-services.json new file mode 100644 index 000000000..1460ea252 --- /dev/null +++ b/zmsentities/schema/citizenapi/offices-and-services.json @@ -0,0 +1,154 @@ +{ + "type": "object", + "description": "Contains information about offices, services, and their relations. Includes details about each office, available services, and how they are related.", + "properties": { + "offices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "$schema", + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": {}, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": {}, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + }, + "required": [ + "id", + "name" + ] + } + }, + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "maxQuantity": { + "type": "number" + }, + "combinable": { + "type": "object", + "properties": { + "10300808": { + "type": "array", + "items": { + "type": "string" + } + }, + "10300814": { + "type": "array", + "items": { + "type": "string" + } + }, + "10300817": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "10300814" + ] + } + }, + "required": [ + "id", + "name", + "maxQuantity" + ] + } + }, + "relations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "officeId": { + "type": "string" + }, + "serviceId": { + "type": "string" + }, + "slots": { + "type": "number" + } + }, + "required": [ + "officeId", + "serviceId", + "slots" + ] + } + } + }, + "required": [ + "offices", + "services", + "relations" + ] +} diff --git a/zmsentities/schema/citizenapi/offices-by-service.json b/zmsentities/schema/citizenapi/offices-by-service.json new file mode 100644 index 000000000..f735809f6 --- /dev/null +++ b/zmsentities/schema/citizenapi/offices-by-service.json @@ -0,0 +1,27 @@ +{ + "type": "object", + "description": "Lists offices associated with specific services.", + "properties": { + "offices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } + } + }, + "required": [ + "offices" + ] +} diff --git a/zmsentities/schema/citizenapi/offices.json b/zmsentities/schema/citizenapi/offices.json new file mode 100644 index 000000000..b281473b2 --- /dev/null +++ b/zmsentities/schema/citizenapi/offices.json @@ -0,0 +1,82 @@ +{ + "type": "object", + "description": "Lists all offices and includes details, such as their IDs, names, and configuration options.", + "properties": { + "offices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "$schema", + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": {}, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": {}, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + }, + "required": [ + "id", + "name" + ] + } + } + }, + "required": [ + "offices" + ] +} diff --git a/zmsentities/schema/citizenapi/scope.json b/zmsentities/schema/citizenapi/scope.json new file mode 100644 index 000000000..36e188a73 --- /dev/null +++ b/zmsentities/schema/citizenapi/scope.json @@ -0,0 +1,68 @@ +{ + "type": "object", + "description": "Defines the scope, which represents a location or unit for processing client requests, including its configuration and availability options.", + "properties": { + "scopes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": { + "type": "number" + }, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": { + "type": "number" + }, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + } + }, + "required": [ + "scopes" + ] + } + \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/scopes.json b/zmsentities/schema/citizenapi/scopes.json new file mode 100644 index 000000000..eb544747b --- /dev/null +++ b/zmsentities/schema/citizenapi/scopes.json @@ -0,0 +1,67 @@ +{ + "type": "object", + "description": "Details a list of all scopes, which are locations or units handling client requests, including their configuration and availability options.", + "properties": { + "scopes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "provider": { + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "$schema", + "id", + "source" + ] + }, + "shortName": { + "type": "string" + }, + "telephoneActivated": { + "type": "string" + }, + "telephoneRequired": {}, + "customTextfieldActivated": { + "type": "string" + }, + "customTextfieldRequired": {}, + "customTextfieldLabel": { + "type": "string" + }, + "captchaActivatedRequired": { + "type": "string" + } + }, + "required": [ + "id", + "provider", + "shortName", + "telephoneActivated", + "telephoneRequired", + "customTextfieldActivated", + "customTextfieldRequired", + "customTextfieldLabel", + "captchaActivatedRequired" + ] + } + } + }, + "required": [ + "scopes" + ] +} diff --git a/zmsentities/schema/citizenapi/services-by-office.json b/zmsentities/schema/citizenapi/services-by-office.json new file mode 100644 index 000000000..96a5cb7df --- /dev/null +++ b/zmsentities/schema/citizenapi/services-by-office.json @@ -0,0 +1,31 @@ +{ + "type": "object", + "description": "Lists services associated with specific offices.", + "properties": { + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "maxQuantity": { + "type": "number" + } + }, + "required": [ + "id", + "name", + "maxQuantity" + ] + } + } + }, + "required": [ + "services" + ] +} diff --git a/zmsentities/schema/citizenapi/services.json b/zmsentities/schema/citizenapi/services.json new file mode 100644 index 000000000..7dff104a4 --- /dev/null +++ b/zmsentities/schema/citizenapi/services.json @@ -0,0 +1,31 @@ +{ + "type": "object", + "description": "Lists all services and includes details, including their IDs, names, and maximum quantities.", + "properties": { + "services": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "maxQuantity": { + "type": "number" + } + }, + "required": [ + "id", + "name", + "maxQuantity" + ] + } + } + }, + "required": [ + "services" + ] +} From 773ae9ae2e9fa5702c4c63fb4086c684e60243f3 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 30 Sep 2024 11:13:33 +0200 Subject: [PATCH 15/42] (feat MPDZBS-877): update local env cli --- cli | 1 + 1 file changed, 1 insertion(+) diff --git a/cli b/cli index ea9191c37..309945c7a 100755 --- a/cli +++ b/cli @@ -22,6 +22,7 @@ modules = [ "zmsadmin", "zmsapi", "zmscalldisplay", + "zmscitizenapi", "zmsclient", "zmsdb", "zmsdldb", From 60b370c74f579554be8ffaa14db9d42a38b83001 Mon Sep 17 00:00:00 2001 From: Thomas Fink <53316058+ThomasAFink@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:54:45 +0200 Subject: [PATCH 16/42] Feature mpdzbs 887 zms 2518 zmscitizenapi post update appointment (#587) * (feat MPDZBS-877) readd citizenapi to htaccess * feat(MPDZBS-877 ZMS-2517): Working Appointment update with too many emails exception * feat(MPDZBS-877 ZMS-2517): Test rendering working for update appointment test * feat(MPDZBS-877 ZMS-2517): Add 63 validation unit tests for appointment update * feat(MPDZBS-877 ZMS-2517): Refactor exception appointmentNotFound * feat(MPDZBS-877 ZMS-2517): Add two new edge case exception tests * feat(MPDZBS-877 ZMS-2517): Remove error_logs --------- Co-authored-by: Thomas Fink --- .htaccess | 8 + .../src/Zmscitizenapi/AppointmentUpdate.php | 52 +- .../Services/ExceptionService.php | 25 +- .../Services/ValidationService.php | 79 +- .../Services/ZmsApiClientService.php | 55 +- .../Services/ZmsApiFacadeService.php | 46 +- .../Zmscitizenapi/AppointmentGetTest.php | 35 +- .../Zmscitizenapi/AppointmentReserveTest.php | 10 +- .../Zmscitizenapi/AppointmentUpdateTest.php | 1789 ++++++++++++++++- .../fixtures/GET_appointments_free.json | 2 +- ...lot.json => POST_reserve_appointment.json} | 10 +- .../fixtures/POST_update_appointment.json | 166 ++ 12 files changed, 2197 insertions(+), 80 deletions(-) rename zmscitizenapi/tests/Zmscitizenapi/fixtures/{POST_reserve_timeslot.json => POST_reserve_appointment.json} (94%) create mode 100644 zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_update_appointment.json diff --git a/.htaccess b/.htaccess index 8826a3601..8f2e4166c 100644 --- a/.htaccess +++ b/.htaccess @@ -39,6 +39,14 @@ RewriteRule ^terminvereinbarung/calldisplay(.*) /var/www/html/zmscalldisplay/pub RewriteRule ^terminvereinbarung/calldisplay/+_(.*) /var/www/html/zmscalldisplay/public/_$1 [QSA] +# zmscitizenapi +SetEnvIf Request_URI ^/zmscitizenapi ZMS_MODULE_BASEPATH=/terminvereinbarung/api/citizen +RewriteCond %{REQUEST_URI} !^/terminvereinbarung/api/citizen/+(_|doc) +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^terminvereinbarung/api/citizen(.*) /var/www/html/zmscitizenapi/public/index.php?$1 [QSA] +RewriteRule ^terminvereinbarung/api/citizen/+(doc|_)(.*) /var/www/html/zmscitizenapi/public/$1$2 [QSA] + + # zmsstatistic SetEnvIf Request_URI ^/zmsstatistic ZMS_MODULE_BASEPATH=/terminvereinbarung/statistic RewriteCond %{REQUEST_URI} !^/terminvereinbarung/[^/]+/+_ diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index 09a6fe3b0..3fb793ee1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -3,14 +3,60 @@ namespace BO\Zmscitizenapi; use BO\Zmscitizenapi\BaseController; -use BO\Slim\Render; +use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use BO\Zmscitizenapi\Services\ValidationService; +use BO\Zmscitizenapi\Helper\UtilityHelper; +use BO\Zmscitizenapi\Services\ExceptionService; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; class AppointmentUpdate extends BaseController { public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - return Render::withJson($response, []); + $request = $request instanceof ServerRequestInterface ? $request : null; + + $body = $request->getParsedBody(); + $processId = $body['processId'] ?? null; + $authKey = $body['authKey'] ?? null; + $familyName = $body['familyName'] ?? null; + $email = $body['email'] ?? null; + $telephone = $body['telephone'] ?? null; + $customTextfield = $body['customTextfield'] ?? null; + + $errors = ValidationService::validateUpdateAppointmentInputs($processId, $authKey, $familyName, $email, $telephone, $customTextfield); + if (!empty($errors['errors'])) { + return $this->createJsonResponse($response, $errors, 400); + } + + try { + + $reservedProcess = ZmsApiFacadeService::getProcessById($processId, $authKey); + if (!empty($reservedProcess['errors'])) { + return $this->createJsonResponse($response, $reservedProcess, 404); + } + + $reservedProcess['clients'][0]['familyName'] = $familyName; + $reservedProcess['clients'][0]['email'] = $email; + $reservedProcess['clients'][0]['telephone'] = $telephone; + $reservedProcess['customTextfield'] = $customTextfield; + + $updatedProcess = ZmsApiFacadeService::updateClientData($reservedProcess); + + if (isset($updatedProcess['exception']) && $updatedProcess['exception'] === 'tooManyAppointmentsWithSameMail') { + return $this->createJsonResponse($response, ExceptionService::tooManyAppointmentsWithSameMail(), 406); + } + + $thinnedProcessData = UtilityHelper::getThinnedProcessData($updatedProcess); + return $this->createJsonResponse($response, $thinnedProcessData, 200); + + } catch (\Exception $e) { + return [ + 'errorCode' => 'unexpectedError', + 'errorMessage' => 'Unexpected error: ' . $e->getMessage(), + 'status' => 500, + ]; + } } -} +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php index dec1e7a9a..e5efadd5b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php @@ -5,7 +5,7 @@ class ExceptionService { - public static function exceptionNoAppointmentsAtLocation(){ + public static function noAppointmentsAtLocation(){ $errors[] = [ 'errorCode' => 'noAppointmentForThisOffice', @@ -17,4 +17,27 @@ public static function exceptionNoAppointmentsAtLocation(){ } + public static function appointmentNotFound(){ + + $errors[] = [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ]; + + return ['errors' => $errors, 'status' => 404]; + + } + + public static function tooManyAppointmentsWithSameMail(){ + $errors[] = [ + 'errorCode' => 'tooManyAppointmentsWithSameMail', + 'errorMessage' => 'Zu viele Termine mit gleicher E-Mail- Adresse.', + 'status' => 406, + ]; + + return ['errors' => $errors, 'status' => 406]; + + } + } \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 91b6c01fc..21da3485a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -83,28 +83,28 @@ public static function validateGetAvailableAppointments($date, $officeId, $servi 'errorMessage' => 'date is required and must be a valid date.', ]; } - + if (!$officeId || !is_numeric($officeId)) { $errors[] = [ 'status' => 400, 'errorMessage' => 'officeId should be a 32-bit integer.', ]; } - + if (empty($serviceIds[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceIds))) { $errors[] = [ 'status' => 400, 'errorMessage' => 'serviceId should be a comma-separated string of integers.', ]; } - + if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { $errors[] = [ 'status' => 400, 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', ]; } - + return ['errors' => $errors, 'status' => 400]; } public static function validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $captchaSolution, $timestamp) @@ -184,7 +184,8 @@ public static function validateGetScopeByIds($scopeIds) return ['errors' => $errors, 'status' => 400]; } - public static function validateGetServicesByOfficeIds($officeIds){ + public static function validateGetServicesByOfficeIds($officeIds) + { $errors = []; if (empty($officeIds) || $officeIds == ['']) { @@ -234,7 +235,7 @@ public static function validateGetProcessNotFound($process) if (!$process) { $errors[] = [ 'errorCode' => 'appointmentNotAvailable', - 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', + 'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.', 'status' => 404, ]; } @@ -248,7 +249,7 @@ public static function validateScopesNotFound($scopes) if (empty($scopes)) { $errors[] = [ 'errorCode' => 'scopesNotFound', - 'errorMessage' => 'Scope(s) not found.', + 'errorMessage' => 'Scope(s) not found.', 'status' => 404, ]; } @@ -262,7 +263,7 @@ public static function validateServicesNotFound($services) if (empty($services)) { $errors[] = [ 'errorCode' => 'servicesNotFound', - 'errorMessage' => 'Service(s) not found for the provided officeId(s).', + 'errorMessage' => 'Service(s) not found for the provided officeId(s).', 'status' => 404, ]; } @@ -276,7 +277,7 @@ public static function validateOfficesNotFound($offices) if (empty($offices)) { $errors[] = [ 'errorCode' => 'officesNotFound', - 'errorMessage' => 'Office(s) not found for the provided serviceId(s).', + 'errorMessage' => 'Office(s) not found for the provided serviceId(s).', 'status' => 404, ]; } @@ -298,7 +299,8 @@ public static function validateAppointmentDaysNotFound($formattedDays) return ['errors' => $errors, 'status' => 404]; } - public static function validateNoAppointmentsAtLocation(){ + public static function validateNoAppointmentsAtLocation() + { $errors[] = [ 'errorCode' => 'noAppointmentForThisScope', @@ -310,4 +312,59 @@ public static function validateNoAppointmentsAtLocation(){ } -} \ No newline at end of file + public static function validateUpdateAppointmentInputs($processId, $authKey, $familyName, $email, $telephone, $customTextfield) + { + $errors = []; + + if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'processId should be a positive 32-bit integer.', + ]; + } + + if (!$authKey || !is_string($authKey)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'authKey should be a non-empty string.', + ]; + } + + if (!$familyName || !is_string($familyName)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'familyName should be a non-empty string.', + ]; + } + + if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'email should be a valid email address.', + ]; + } + + if ($telephone !== null && !$telephone || !preg_match('/^\d{7,15}$/', $telephone)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.', + ]; + } + + if ($customTextfield !== null && !is_string($customTextfield)) { + $errors[] = [ + 'status' => 400, + 'errorMessage' => 'customTextfield should be a string.', + ]; + } + + if (!empty($errors)) { + return ['errors' => $errors, 'status' => 400]; + } + + return ['status' => 200, 'message' => 'Valid input for updating appointment.']; + } + + +} + diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index 4f5d0519e..aa05bfa53 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -44,7 +44,8 @@ public static function getServices() } - public static function getRequestRelationList(){ + public static function getRequestRelationList() + { $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, @@ -89,15 +90,14 @@ public static function getFreeTimeslots($providers, $requests, $firstDay, $lastD } $psr7Response = $result->getResponse(); - $responseBody = (string) $psr7Response->getBody(); - + return $result; } public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) { $requests = []; - + foreach ($serviceIds as $index => $serviceId) { $count = intval($serviceCounts[$index]); for ($i = 0; $i < $count; $i++) { @@ -109,32 +109,61 @@ public static function reserveTimeslot($appointmentProcess, $serviceIds, $servic } $processEntity = new ProcessEntity(); - + $processEntity->appointments = $appointmentProcess['appointments'] ?? []; $processEntity->authKey = $appointmentProcess['authKey'] ?? null; $processEntity->clients = $appointmentProcess['clients'] ?? []; - + $processEntity->scope = $appointmentProcess['scope'] ?? null; $processEntity->requests = $requests; $processEntity->lastChange = $appointmentProcess['lastChange'] ?? time(); - + $processEntity->createIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; $processEntity->createTimestamp = time(); - + if (isset($appointmentProcess['queue'])) { $processEntity->queue = $appointmentProcess['queue']; } - + $result = \App::$http->readPostResult('/process/status/reserved/', $processEntity); - + return $result->getEntity(); } - public function submitClientData($process) + public static function submitClientData($process) { - $url = "/process/{$process['id']}/{$process['authKey']}/"; - return \App::$http->readPostResult($url, $process)->getEntity(); + $processEntity = new ProcessEntity(); + $processEntity->id = $process['data']['processId'] ?? null; + $processEntity->authKey = $process['data']['authKey'] ?? null; + $processEntity->appointments = $process['appointments'] ?? []; + $processEntity->clients = $process['clients'] ?? []; + $processEntity->scope = $process['data']['scope'] ?? null; + $processEntity->customTextfield = $process['customTextfield'] ?? null; + $processEntity->lastChange = $process['lastChange'] ?? time(); + + if (isset($process['queue'])) { + $processEntity->queue = $process['queue']; + } + + $processEntity->createIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; + $processEntity->createTimestamp = time(); + + $url = "/process/{$processEntity->id}/{$processEntity->authKey}/"; + + try { + $result = \App::$http->readPostResult($url, $processEntity); + return $result->getEntity(); + } catch (\Exception $e) { + $exceptionName = json_decode(json_encode($e), true)['template'] ?? null; + if ($exceptionName === 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail') { + $exception = [ + 'exception' => 'tooManyAppointmentsWithSameMail' + ]; + return $exception; + } + } + } public function preconfirmProcess($process) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index 4f23c0597..e97ca5f33 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -11,7 +11,7 @@ public static function getOffices() { $scopeList = ZmsApiClientService::getScopes() ?? []; $providerProjectionList = []; - + foreach (ZmsApiClientService::getOffices() as $provider) { $matchingScope = null; foreach ($scopeList as $scope) { @@ -20,12 +20,12 @@ public static function getOffices() break; } } - + $providerData = [ "id" => $provider->id, "name" => $provider->displayName ?? $provider->name, ]; - + if ($matchingScope) { $providerData["scope"] = [ "id" => $matchingScope->id, @@ -40,16 +40,16 @@ public static function getOffices() "displayInfo" => $matchingScope->getDisplayInfo() ]; } - + $providerProjectionList[] = $providerData; } - + return [ "offices" => $providerProjectionList, "status" => 200 ]; } - + public static function getScopes() { @@ -429,8 +429,7 @@ public static function getBookableFreeDays(array $queryParams) ]; } catch (\Exception $e) { - //error_log('Error in AvailableDaysService: ' . $e->getMessage()); - return ExceptionService::exceptionNoAppointmentsAtLocation(); + return ExceptionService::noAppointmentsAtLocation(); } } public static function getFreeAppointments(array $params) @@ -462,13 +461,11 @@ public static function getFreeAppointments(array $params) $psr7Response = $freeSlots->getResponse(); $responseBody = (string) $psr7Response->getBody(); - $responseBody = json_decode($responseBody, true); return $responseBody['data']; } catch (\Exception $e) { - //error_log('Error in AvailableAppointmentsService: ' . $e->getMessage()); return [ 'appointmentTimestamps' => [], 'errorCode' => 'internalError', @@ -528,7 +525,7 @@ public static function getAvailableAppointments(array $queryParams) private static function processFreeSlots($freeSlots) { - + $errors = ValidationService::validateGetProcessFreeSlots($freeSlots); if (!empty($errors['errors'])) { return $errors; @@ -593,11 +590,7 @@ public static function getProcessById($processId, $authKey) } catch (\Exception $e) { if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { - return [ - 'errorCode' => 'appointmentNotFound', - 'errorMessage' => 'Termin wurde nicht gefunden.', - 'status' => 404, - ]; + return ExceptionService::appointmentNotFound(); } else { return [ 'errorCode' => 'unexpectedError', @@ -608,13 +601,18 @@ public static function getProcessById($processId, $authKey) } } + public static function updateClientData($reservedProcess) + { + + $clientUpdateResult = ZmsApiClientService::submitClientData($reservedProcess); + + if (!isset($clientUpdateResult['error'])) { + return $clientUpdateResult; + } + + return $clientUpdateResult; + } - /* Todo add method - * updateClientData - * - * - * - */ /* Todo add method * preconfirmAppointment @@ -638,4 +636,6 @@ public static function getProcessById($processId, $authKey) * */ -} \ No newline at end of file +} + + diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php index 858ddc948..6eea60d26 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php @@ -17,7 +17,7 @@ public function testRendering() 'url' => '/process/101002/fb43/', 'parameters' => [ 'resolveReferences' => 2, - ], + ], 'response' => $this->readFixture("GET_process.json") ] ] @@ -29,7 +29,7 @@ public function testRendering() ]; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ 'processId' => '101002', 'timestamp' => 1724907600, @@ -165,7 +165,7 @@ public function testMissingProcessId() ]; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ 'errors' => [ [ @@ -186,7 +186,7 @@ public function testMissingAuthKey() ]; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ 'errors' => [ [ @@ -208,7 +208,7 @@ public function testInvalidProcessId() ]; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ 'errors' => [ [ @@ -230,7 +230,7 @@ public function testInvalidAuthKey() ]; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ 'errors' => [ [ @@ -249,7 +249,7 @@ public function testBothParametersMissing() $parameters = []; $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ 'errors' => [ [ @@ -277,26 +277,31 @@ public function testAppointmentNotFound() 'url' => '/process/101002/fb43/', 'parameters' => [ 'resolveReferences' => 2, - ], + ], 'exception' => new \Exception('API-Error: Zu den angegebenen Daten konnte kein Termin gefunden werden.') ] ] ); - + $parameters = [ 'processId' => '101002', 'authKey' => 'fb43', ]; - + $response = $this->render([], $parameters, []); - $responseBody = json_decode((string)$response->getBody(), true); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ - 'errorCode' => 'appointmentNotFound', - 'errorMessage' => 'Termin wurde nicht gefunden.', - 'status' => 404, + 'errors' => [ + [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ] + ], + 'status' => 404 ]; $this->assertEquals(404, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } - + } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php index 45dfe6e0c..a35d6b75e 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -28,7 +28,7 @@ public function testRendering() [ 'function' => 'readPostResult', 'url' => '/process/status/reserved/', - 'response' => $this->readFixture("POST_reserve_timeslot.json") + 'response' => $this->readFixture("POST_reserve_appointment.json") ] ] ); @@ -44,13 +44,13 @@ public function testRendering() $response = $this->render([], $parameters, [], 'POST'); $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ - 'processId' => '101142', + 'processId' => '101002', 'timestamp' => 32526616522, - 'authKey' => 'b93e', - 'familyName' => '', + 'authKey' => 'fb43', + 'familyName' => 'Smith', 'customTextfield' => '', 'email' => 'test@muenchen.de', - 'telephone' => '', + 'telephone' => '123456789', 'officeName' => null, 'officeId' => '10546', 'scope' => [ diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php index 48279fd9f..f079b9ac6 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -7,8 +7,1791 @@ class AppointmentUpdateTest extends Base protected $classname = "AppointmentUpdate"; - public function testRendering() { - $responseData = $this->renderJson(method: 'POST'); - $this->assertEqualsCanonicalizing([], $responseData); + public function testRendering() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101002/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_process.json") + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/101002/fb43/', + 'response' => $this->readFixture("POST_update_appointment.json") + ] + ] + ); + + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => "test@muenchen.de", + 'telephone' => '123456789', + 'customTextfield' => "Some custom text", + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + "processId" => "101002", + "timestamp" => 1727865900, + "authKey" => "fb43", + "familyName" => "Smith", + "customTextfield" => "Some custom text", + "email" => "test@muenchen.de", + "telephone" => "123456789", + "officeName" => null, + "officeId" => null, + "scope" => [ + '$schema' => "https://schema.berlin.de/queuemanagement/scope.json", + "id" => 0, + "source" => "dldb" + ], + "subRequestCounts" => [], + "serviceId" => "10242339", + "serviceCount" => 1 + ]; + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + + } + + public function testTooManyEmailsAtLocation() + { + + $exception = new \BO\Zmsclient\Exception(); + $exception->template = 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail'; + + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101002/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'response' => $this->readFixture("GET_process.json") + ], + [ + 'function' => 'readPostResult', + 'url' => '/process/101002/fb43/', + 'exception' => $exception + ] + ] + ); + + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => "test@muenchen.de", + 'telephone' => '123456789', + 'customTextfield' => "Some custom text", + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'tooManyAppointmentsWithSameMail', + 'errorMessage' => 'Zu viele Termine mit gleicher E-Mail- Adresse.', + 'status' => 406, + ] + ], + 'status' => 406 + ]; + + $this->assertEquals(406, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testAppointmentNotFound() + { + $this->setApiCalls( + [ + [ + 'function' => 'readGetResult', + 'url' => '/process/101003/fb43/', + 'parameters' => [ + 'resolveReferences' => 2, + ], + 'exception' => new \Exception('API-Error: Zu den angegebenen Daten konnte kein Termin gefunden werden.') + ] + ] + ); + + $parameters = [ + 'processId' => '101003', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => "test@muenchen.de", + 'telephone' => '123456789', + 'customTextfield' => "Some custom text", + ]; + + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + $expectedResponse = [ + 'errors' => [ + [ + 'errorCode' => 'appointmentNotFound', + 'errorMessage' => 'Termin wurde nicht gefunden.', + 'status' => 404, + ] + ], + 'status' => 404 + ]; + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => null, + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => '', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } + + public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => '', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_ValidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'invalid-email', + 'telephone' => '123456789', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_InvalidTelephone_ValidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123', + 'customTextfield' => 'Some custom text' + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + + public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_ValidTelephone_InvalidCustomtextfield() + { + $parameters = [ + 'processId' => '101002', + 'authKey' => 'fb43', + 'familyName' => 'Smith', + 'email' => 'test@muenchen.de', + 'telephone' => '123456789', + 'customTextfield' => 123 + ]; + $response = $this->render([], $parameters, [], 'POST'); + $responseBody = json_decode((string) $response->getBody(), true); + + $expectedResponse = [ + 'errors' => [ + ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ], + 'status' => 400 + ]; + + $this->assertEquals($expectedResponse['status'], $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); + } + } diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json index 322b9e35d..1004d3fcb 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json @@ -58,7 +58,7 @@ "apiclient": { "shortname": "default" }, - "authKey": "", + "authKey": "fb43", "createIP": "", "createTimestamp": 1725635464, "id": 0, diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_appointment.json similarity index 94% rename from zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json rename to zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_appointment.json index f3d8f1840..9271cc127 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_timeslot.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_reserve_appointment.json @@ -56,21 +56,21 @@ "apiclient": { "shortname": "default" }, - "authKey": "b93e", + "authKey": "fb43", "displayInfo": "Infos zum Standort.", "clients": [ { - "familyName": "", + "familyName": "Smith", "email": "test@muenchen.de", "emailSendCount": "0", "notificationsSendCount": "0", "surveyAccepted": "0", - "telephone": "" + "telephone": "123456789" } ], "createIP": "", "createTimestamp": 1727189317, - "id": "101142", + "id": "101002", "serviceCount": 1, "archiveId": 0, "queue": { @@ -78,7 +78,7 @@ "arrivalTime": 1727691300, "callCount": "0", "callTime": 0, - "number": "101142", + "number": "101002", "waitingTimeEstimate": 0, "waitingTimeOptimistic": 0, "waitingTime": null, diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_update_appointment.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_update_appointment.json new file mode 100644 index 000000000..065a56d58 --- /dev/null +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/POST_update_appointment.json @@ -0,0 +1,166 @@ +{ + "$schema": "https://localhost/terminvereinbarung/api/2/", + "meta": { + "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", + "error": false, + "generated": "2024-09-24T16:48:37+02:00", + "server": "Zmsapi-ENV ()" + }, + "data": { + "$schema": "https://schema.berlin.de/queuemanagement/process.json", + "amendment": "", + "customTextfield": "Some custom text", + "appointments": [ + { + "date": 1727865900, + "scope": { + "id": "228", + "source": "dldb" + }, + "availability": { + "id": "6712", + "weekday": { + "sunday": "0", + "monday": "0", + "tuesday": "0", + "wednesday": "8", + "thursday": "0", + "friday": "0", + "saturday": "0" + }, + "repeat": { + "afterWeeks": "1", + "weekOfMonth": "0" + }, + "bookable": { + "startInDays": "0", + "endInDays": "30" + }, + "workstationCount": { + "public": "1", + "callcenter": "0", + "intern": "1" + }, + "lastChange": 1727247741, + "multipleSlotsAllowed": "1", + "slotTimeInMinutes": "5", + "startDate": 1727820000, + "endDate": 1735599600, + "startTime": "07:30:00", + "endTime": "12:50:00", + "type": "appointment", + "scope": { + "id": "228" + }, + "description": "Terminserie-Pass Mittwoch" + }, + "slotCount": "1" + } + ], + "apiclient": { + "shortname": "default" + }, + "authKey": "fb43", + "clients": [ + { + "familyName": "Smith", + "email": "test@muenchen.de", + "emailSendCount": "0", + "notificationsSendCount": "0", + "surveyAccepted": "0", + "telephone": "123456789" + } + ], + "createIP": "172.19.0.1", + "createTimestamp": "1727853590", + "id": "101002", + "archiveId": 0, + "queue": { + "$schema": "https://schema.berlin.de/queuemanagement/queue.json", + "arrivalTime": 1727865900, + "callCount": "0", + "callTime": 0, + "number": "101002", + "waitingTimeEstimate": 0, + "waitingTimeOptimistic": 0, + "waitingTime": null, + "wayTime": null, + "status": "confirmed", + "lastCallTime": 0, + "destination": null, + "destinationHint": null, + "withAppointment": "1" + }, + "reminderTimestamp": "0", + "requests": [ + { + "id": "10242339", + "link": "https://service.berlin.de/dienstleistung/10242339/", + "name": "Adressänderung Personalausweis, Reisepass, eAT", + "group": "Sonstiges", + "source": "dldb", + "data": { + "authorities": [ + { + "id": "1", + "name": "X", + "webinfo": "https://www.berlin.de/sen/finanzen/" + } + ], + "locations": [ + { + "location": "102522", + "authority": { + "id": "1", + "name": "X", + "webinfo": "https://www.berlin.de/sen/finanzen/" + }, + "url": "https://zms-demo.muenchen.de/buergeransicht/#/services/10242339/locations/102522", + "appointment": { + "link": "https://zms-demo.muenchen.de/buergeransicht/#/services/10242339/locations/102522", + "slots": "0", + "external": false, + "multiple": "1", + "allowed": true + }, + "hint": false + } + ], + "meta": { + "lastupdate": "2024-09-25T10:02:05", + "url": "https://zms-demo.muenchen.de/buergeransicht/#/services/10242339", + "locale": "de", + "keywords": "", + "translated": true, + "hash": "", + "id": "10242339" + }, + "id": "10242339", + "description": "

Es empfiehlt sich, die Adresse in sämtlichen Ausweisdokumenten gleichzeitig mit der Anmeldung zu ändern.

", + "links": [ + { + "name": "Datenschutzgrundverordnung", + "link": "https://stadt.muenchen.de/infos/dsgvo-datenschutzgrundverordnung.html", + "description": false + } + ], + "forms": [ + { + "name": "Datenschutzgrundverordnung", + "link": "https://stadt.muenchen.de/infos/dsgvo-datenschutzgrundverordnung.html", + "description": false + } + ], + "name": "Adressänderung Personalausweis, Reisepass, eAT", + "appointment": { + "link": "https://zms-demo.muenchen.de/buergeransicht/#/services/10242339" + }, + "maxQuantity": 5, + "public": true, + "fees": "gebührenfrei", + "duration": 5 + } + } + ] + } +} \ No newline at end of file From 853cf7ca3227068a8aa388a26898005534d35e78 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 6 Nov 2024 10:37:47 +0100 Subject: [PATCH 17/42] feat(MPDZBS-877): update .gitignore --- zmscitizenapi/.gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zmscitizenapi/.gitignore b/zmscitizenapi/.gitignore index 460d49306..dbf690fac 100644 --- a/zmscitizenapi/.gitignore +++ b/zmscitizenapi/.gitignore @@ -3,7 +3,7 @@ vendor vendor.zmsbase config.php tags -.DS_Store +.DS_* public/_tests public/doc/assets/redoc.min.js public/doc/swagger.* @@ -13,7 +13,5 @@ public/_test/assets/swagger-ui-standalone-preset.js cache vendor.old vendor.update -#composer.lock VERSION -package-lock.json .phpunit.result.cache \ No newline at end of file From 348785e7f16446dc37cea854b4d134cb0690845e Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 6 Nov 2024 15:39:02 +0100 Subject: [PATCH 18/42] cleanup(MPDZBS-877): Improve security and cleanup code --- mellon/phpunit.xml.dist | 2 +- .../fixtures/GET_processList_empty.json | 2 +- .../fixtures/GET_processList_fake_entry.json | 2 +- .../fixtures/GET_source_unittest.json | 2 +- zmsapi/public/doc/README.md | 4 -- zmscalldisplay/phpunit.xml | 2 +- zmscitizenapi/Makefile | 2 +- zmscitizenapi/bin/configure | 53 ++++++++++---- zmscitizenapi/bootstrap.php | 5 ++ zmscitizenapi/config.example.php | 16 ++++- .../src/Zmscitizenapi/Application.php | 11 +-- .../src/Zmscitizenapi/AppointmentReserve.php | 2 +- .../src/Zmscitizenapi/BaseController.php | 7 +- .../src/Zmscitizenapi/Helper/ErrorHandler.php | 72 +++++++++++++++++-- .../Zmscitizenapi/Helper/UtilityHelper.php | 38 ++++++---- .../Services/ExceptionService.php | 25 ++++--- .../Zmscitizenapi/AppointmentGetTest.php | 12 ++-- .../Zmscitizenapi/AppointmentReserveTest.php | 30 ++++---- .../Zmscitizenapi/AppointmentUpdateTest.php | 72 +++++++++---------- .../AvailableAppointmentsListTest.php | 22 +++--- .../Zmscitizenapi/AvailableDaysListTest.php | 8 +-- .../tests/Zmscitizenapi/CaptchaGetTest.php | 8 +-- .../tests/Zmscitizenapi/bootstrap.php | 1 - .../fixtures/GET_SourceGet_dldb.json | 2 +- .../Zmscitizenapi/fixtures/GET_calendar.json | 2 +- .../fixtures/GET_calendar_empty_days.json | 2 +- .../Zmscitizenapi/fixtures/GET_process.json | 2 +- .../fixtures/GET_reserve_SourceGet_dldb.json | 2 +- .../fixtures/POST_reserve_appointment.json | 2 +- .../fixtures/POST_update_appointment.json | 2 +- zmsclient/phpunit.xml | 2 +- zmsdb/paratest.xml | 2 +- zmsdldb/phpunit.xml.dist | 2 +- .../schema/citizenapi/appointment-cancel.json | 24 ++++--- .../citizenapi/appointment-reserve.json | 5 +- .../schema/citizenapi/appointment.json | 3 +- .../schema/citizenapi/available-days.json | 5 +- .../schema/citizenapi/captcha-details.json | 7 +- .../citizenapi/offices-and-services.json | 14 ++-- zmsentities/schema/citizenapi/offices.json | 8 ++- zmsentities/schema/citizenapi/scopes.json | 8 ++- .../schema/citizenapi/services-by-office.json | 13 +++- zmsentities/schema/citizenapi/services.json | 13 +++- zmsslim/phpunit.xml.dist | 2 +- 44 files changed, 342 insertions(+), 178 deletions(-) diff --git a/mellon/phpunit.xml.dist b/mellon/phpunit.xml.dist index 73763f0ef..24f093133 100644 --- a/mellon/phpunit.xml.dist +++ b/mellon/phpunit.xml.dist @@ -10,7 +10,7 @@ convertWarningsToExceptions="true" processIsolation="false" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" bootstrap="tests/bootstrap.php" > diff --git a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json index 70a75536d..ebd499321 100644 --- a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json +++ b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_empty.json @@ -4,7 +4,7 @@ "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", "error": false, "generated": "2019-02-11T16:07:58+01:00", - "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + "server": "Zmsapi" }, "data": [] } \ No newline at end of file diff --git a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json index b77ee7d25..2917b18ea 100644 --- a/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json +++ b/zmsadmin/tests/Zmsadmin/fixtures/GET_processList_fake_entry.json @@ -4,7 +4,7 @@ "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", "error": false, "generated": "2019-02-11T16:07:58+01:00", - "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + "server": "Zmsapi" }, "data": [ { diff --git a/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json b/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json index bee3c0341..bb0f11de7 100644 --- a/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json +++ b/zmsadmin/tests/Zmsadmin/fixtures/GET_source_unittest.json @@ -4,7 +4,7 @@ "$schema": "https://schema.berlin.de/queuemanagement/metaresult.json", "error": false, "generated": "2019-02-08T14:45:15+01:00", - "server": "Zmsapi-ENV (v2.19.02-38-g0ba2cba)" + "server": "Zmsapi" }, "data": { "$schema": "https://schema.berlin.de/queuemanagement/source.json", diff --git a/zmsapi/public/doc/README.md b/zmsapi/public/doc/README.md index 648f5276d..4ed6602b2 100644 --- a/zmsapi/public/doc/README.md +++ b/zmsapi/public/doc/README.md @@ -1,7 +1,6 @@ ## How does the Open Api definition work ## Version 2.0 -<<<<<<< HEAD ``` bin/configure npm i @@ -18,9 +17,6 @@ https://zms.ddev.site/terminvereinbarung/api/2/doc/index.html https://it-at-m.github.io/eappointment/zmsapi/public/doc/index.html ``` - -======= ->>>>>>> next * Under /public/doc are the schema from zmsentities. A symbolic link points to the corresponding folder under vendor/eappointment/zmsentities/schema. * Under /bin there is a build_swagger.js file. This is executed via ``npm run doc`` and validates the existing swagger.yaml file. If valid, the open api annotations are read from routing.php and the remaining information such as info, definitions, version and tags are compiled from the yaml files under ./partials into a complete swagger.yaml. diff --git a/zmscalldisplay/phpunit.xml b/zmscalldisplay/phpunit.xml index aae7f1a39..d4fd86860 100644 --- a/zmscalldisplay/phpunit.xml +++ b/zmscalldisplay/phpunit.xml @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" verbose="true" processIsolation="false" > diff --git a/zmscitizenapi/Makefile b/zmscitizenapi/Makefile index 7339248b1..896703b0a 100644 --- a/zmscitizenapi/Makefile +++ b/zmscitizenapi/Makefile @@ -1,4 +1,4 @@ -.PHONY: help now dev live watch +.PHONY: help now dev live watch fix openapi coverage paratest help: # This help @echo "Possible Targets:" diff --git a/zmscitizenapi/bin/configure b/zmscitizenapi/bin/configure index 30144d9d3..8cc6d841f 100755 --- a/zmscitizenapi/bin/configure +++ b/zmscitizenapi/bin/configure @@ -1,28 +1,51 @@ #!/bin/bash +# Resolve real path for scripts using either realpath or readlink REALPATH=$(which realpath || which readlink) REALPATH=$([[ "$REALPATH" =~ 'readlink' ]] && echo "$REALPATH -e" || echo "$REALPATH") DIR=$(dirname $($REALPATH ${BASH_SOURCE[0]})) ROOT=$($REALPATH $DIR/..) -#echo "Get DLDB Exports" -#$ROOT/bin/getDldbExport - echo -n "Configuring application" -mkdir -p $ROOT/.git/hooks -ln -sf $ROOT/bin/test $ROOT/.git/hooks/pre-commit -ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-checkout -ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-commit -ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-merge -if [ ! -e $ROOT/config.php ] -then - cp $ROOT/config.example.php $ROOT/config.php +# Create .git/hooks directory and symlink hook scripts +mkdir -p "$ROOT/.git/hooks" +ln -sf "$ROOT/bin/test" "$ROOT/.git/hooks/pre-commit" +ln -sf "$ROOT/bin/configure" "$ROOT/.git/hooks/post-checkout" +ln -sf "$ROOT/bin/configure" "$ROOT/.git/hooks/post-commit" +ln -sf "$ROOT/bin/configure" "$ROOT/.git/hooks/post-merge" + +# Initialize configuration file +if [[ ! -f "$ROOT/config.php" ]]; then + # Check if config.example.php exists + if [[ ! -f "$ROOT/config.example.php" ]]; then + echo "Error: config.example.php not found" >&2 + exit 1 + fi + # Try to copy config.example.php to config.php + if ! cp "$ROOT/config.example.php" "$ROOT/config.php"; then + echo "Error: Failed to create config.php" >&2 + exit 1 + fi + # Set secure permissions on config.php + if ! chmod 640 "$ROOT/config.php"; then + echo "Warning: Failed to set secure permissions on config.php" >&2 + fi fi -test -d $ROOT/cache && chmod -fR a+rwX $ROOT/cache || echo "Could not chmod cache files" +# Ensure cache directory exists +if [[ ! -d "$ROOT/cache" ]]; then + echo "Error: Cache directory not found" >&2 + exit 1 +fi + +# Set secure permissions on cache directory +if ! chmod -R u=rwX,g=rwX,o=rX "$ROOT/cache"; then + echo "Error: Failed to set cache directory permissions" >&2 + exit 1 +fi -#VERSION=`git symbolic-ref -q --short HEAD || git describe --tags --exact-match` -VERSION=`git describe --tags --always` -echo $VERSION > $ROOT/VERSION +# Set and save the application version +VERSION=$(git describe --tags --always) +echo "$VERSION" > "$ROOT/VERSION" echo " $VERSION" diff --git a/zmscitizenapi/bootstrap.php b/zmscitizenapi/bootstrap.php index 708e14373..21492f377 100644 --- a/zmscitizenapi/bootstrap.php +++ b/zmscitizenapi/bootstrap.php @@ -1,4 +1,9 @@ 'test@muenchen.de' + 'email' => 'default@example.com' ] ]; diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 13da97818..85bb384b2 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -15,8 +15,13 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, } /** - * @codeCoverageIgnore + * Hook method for handling responses in child controllers. + * Child classes should override this method to implement their specific response logic. * + * @param RequestInterface $request The HTTP request + * @param ResponseInterface $response The HTTP response + * @param array $args Route parameters + * @return ResponseInterface The modified response */ public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php index 277927929..1e8ae6bf1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php @@ -9,8 +9,72 @@ class ErrorHandler implements ErrorHandlerInterface { - public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails): ResponseInterface - { - // TODO: Implement __invoke() method. - } + /** + * Handle errors and exceptions in a standardized way. + * + * @param ServerRequestInterface $request The current request + * @param Throwable $exception The exception that was thrown + * @param bool $displayErrorDetails Whether to display error details + * @param bool $logErrors Whether to log errors + * @param bool $logErrorDetails Whether to log error details + */ + public function __invoke( + ServerRequestInterface $request, + Throwable $exception, + bool $displayErrorDetails, + bool $logErrors, + bool $logErrorDetails + ): ResponseInterface { + $statusCode = 500; + if ($exception instanceof HttpException) { + $statusCode = $exception->getCode(); + } + + $error = [ + 'message' => $this->getErrorMessage($exception, $displayErrorDetails), + 'code' => $statusCode, + ]; + + if ($displayErrorDetails) { + $error['trace'] = $exception->getTraceAsString(); + } + + if ($logErrors) { + $this->logError($exception, $logErrorDetails); + } + + $payload = json_encode($error, JSON_PRETTY_PRINT); + + $response = new Response(); + $response->getBody()->write($payload); + + return $response + ->withStatus($statusCode) + ->withHeader('Content-Type', 'application/json'); + } + + /** + * Get appropriate error message based on environment. + */ + private function getErrorMessage(Throwable $exception, bool $displayErrorDetails): string + { + if ($displayErrorDetails) { + return $exception->getMessage(); + } + + // Generic message in production + return 'An error has occurred. Please try again later.'; + } + + /** + * Log the error with appropriate detail level. + */ + private function logError(Throwable $exception, bool $logErrorDetails): void + { + $message = $exception->getMessage(); + if ($logErrorDetails) { + $message .= "\n" . $exception->getTraceAsString(); + } + error_log($message); + } } \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php index e3b772ee1..a6efa814a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php @@ -4,9 +4,9 @@ class UtilityHelper { - public static function getInternalDateFromISO($dateString) + + private static function formatDateArray(\DateTime $date): array { - $date = new \DateTime($dateString); return [ 'day' => (int) $date->format('d'), 'month' => (int) $date->format('m'), @@ -14,14 +14,27 @@ public static function getInternalDateFromISO($dateString) ]; } + public static function getInternalDateFromISO($dateString) + { + try { + if (!is_string($dateString)) { + throw new \InvalidArgumentException('Date string must be a string'); + } + $date = new \DateTime($dateString); + return self::formatDateArray($date); + } catch (\Exception $e) { + throw new \InvalidArgumentException('Invalid ISO date format: ' . $e->getMessage()); + } + } + public static function getInternalDateFromTimestamp(int $timestamp) { - $date = (new \DateTime())->setTimestamp($timestamp); - return [ - 'day' => (int) $date->format('d'), - 'month' => (int) $date->format('m'), - 'year' => (int) $date->format('Y') - ]; + try { + $date = (new \DateTime())->setTimestamp($timestamp); + return self::formatDateArray($date); + } catch (\Exception $e) { + throw new \InvalidArgumentException('Invalid timestamp: ' . $e->getMessage()); + } } public static function uniqueElementsFilter($value, $index, $self) @@ -29,7 +42,7 @@ public static function uniqueElementsFilter($value, $index, $self) return array_search($value, $self) === $index; } - public static function getThinnedProcessData($myProcess) + public static function getThinnedProcessData(?object $myProcess): array { if (!$myProcess || !isset($myProcess->id)) { return []; @@ -39,8 +52,9 @@ public static function getThinnedProcessData($myProcess) $mainServiceId = null; $mainServiceCount = 0; - if (isset($myProcess->requests)) { - $requests = is_array($myProcess->requests) ? $myProcess->requests : iterator_to_array($myProcess->requests); + $requests = $myProcess->requests ?? []; + if ($requests) { + $requests = is_array($requests) ? $requests : iterator_to_array($requests); if (count($requests) > 0) { $mainServiceId = $requests[0]->id; foreach ($requests as $request) { @@ -75,5 +89,5 @@ public static function getThinnedProcessData($myProcess) 'serviceCount' => $mainServiceCount, ]; } - + } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php index e5efadd5b..cb52cba27 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php @@ -5,38 +5,45 @@ class ExceptionService { + private const HTTP_NOT_FOUND = 404; + private const HTTP_NOT_ACCEPTABLE = 406; + + private const ERROR_NO_APPOINTMENTS = 'noAppointmentForThisOffice'; + private const ERROR_APPOINTMENT_NOT_FOUND = 'appointmentNotFound'; + private const ERROR_TOO_MANY_APPOINTMENTS = 'tooManyAppointmentsWithSameMail'; + public static function noAppointmentsAtLocation(){ $errors[] = [ - 'errorCode' => 'noAppointmentForThisOffice', + 'errorCode' => self::ERROR_NO_APPOINTMENTS, 'errorMessage' => 'An diesem Standort gibt es aktuell leider keine freien Termine.', - 'status' => 404, + 'status' => self::HTTP_NOT_FOUND, ]; - return ['errors' => $errors, 'status' => 404]; + return ['errors' => $errors, 'status' => self::HTTP_NOT_FOUND]; } public static function appointmentNotFound(){ $errors[] = [ - 'errorCode' => 'appointmentNotFound', + 'errorCode' => self::ERROR_APPOINTMENT_NOT_FOUND, 'errorMessage' => 'Termin wurde nicht gefunden.', - 'status' => 404, + 'status' => self::HTTP_NOT_FOUND, ]; - return ['errors' => $errors, 'status' => 404]; + return ['errors' => $errors, 'status' => self::HTTP_NOT_FOUND]; } public static function tooManyAppointmentsWithSameMail(){ $errors[] = [ - 'errorCode' => 'tooManyAppointmentsWithSameMail', + 'errorCode' => self::ERROR_TOO_MANY_APPOINTMENTS, 'errorMessage' => 'Zu viele Termine mit gleicher E-Mail- Adresse.', - 'status' => 406, + 'status' => self::HTTP_NOT_ACCEPTABLE, ]; - return ['errors' => $errors, 'status' => 406]; + return ['errors' => $errors, 'status' => self::HTTP_NOT_ACCEPTABLE]; } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php index 6eea60d26..375f9dcac 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php @@ -155,7 +155,7 @@ public function testRendering() 'serviceCount' => 1 ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingProcessId() @@ -176,7 +176,7 @@ public function testMissingProcessId() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingAuthKey() @@ -197,7 +197,7 @@ public function testMissingAuthKey() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidProcessId() @@ -219,7 +219,7 @@ public function testInvalidProcessId() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidAuthKey() @@ -241,7 +241,7 @@ public function testInvalidAuthKey() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testBothParametersMissing() @@ -264,7 +264,7 @@ public function testBothParametersMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php index a35d6b75e..6f7959a5d 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -49,7 +49,7 @@ public function testRendering() 'authKey' => 'fb43', 'familyName' => 'Smith', 'customTextfield' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'officeName' => null, 'officeId' => '10546', @@ -73,7 +73,7 @@ public function testRendering() 'serviceCount' => 0 ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testAppointmentNotAvailable() @@ -117,7 +117,7 @@ public function testAppointmentNotAvailable() 'status' => 404 ]; $this->assertEquals(404, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingOfficeId() @@ -143,7 +143,7 @@ public function testMissingOfficeId() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingServiceId() @@ -169,7 +169,7 @@ public function testMissingServiceId() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingTimestamp() @@ -195,7 +195,7 @@ public function testMissingTimestamp() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingOfficeIdAndServiceId() @@ -224,7 +224,7 @@ public function testMissingOfficeIdAndServiceId() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingOfficeIdAndTimestamp() @@ -253,7 +253,7 @@ public function testMissingOfficeIdAndTimestamp() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingServiceIdAndTimestamp() @@ -282,7 +282,7 @@ public function testMissingServiceIdAndTimestamp() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingAllFields() @@ -311,7 +311,7 @@ public function testMissingAllFields() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidOfficeIdFormat() @@ -338,7 +338,7 @@ public function testInvalidOfficeIdFormat() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidServiceIdFormat() @@ -365,7 +365,7 @@ public function testInvalidServiceIdFormat() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidTimestampFormat() @@ -392,7 +392,7 @@ public function testInvalidTimestampFormat() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testEmptyServiceIdArray() { @@ -418,7 +418,7 @@ public function testEmptyServiceIdArray() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidServiceCount() @@ -445,7 +445,7 @@ public function testInvalidServiceCount() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php index f079b9ac6..5b2013734 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -31,7 +31,7 @@ public function testRendering() 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => "test@muenchen.de", + 'email' => "default@example.com", 'telephone' => '123456789', 'customTextfield' => "Some custom text", ]; @@ -44,7 +44,7 @@ public function testRendering() "authKey" => "fb43", "familyName" => "Smith", "customTextfield" => "Some custom text", - "email" => "test@muenchen.de", + "email" => "default@example.com", "telephone" => "123456789", "officeName" => null, "officeId" => null, @@ -59,7 +59,7 @@ public function testRendering() ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } @@ -91,7 +91,7 @@ public function testTooManyEmailsAtLocation() 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => "test@muenchen.de", + 'email' => "default@example.com", 'telephone' => '123456789', 'customTextfield' => "Some custom text", ]; @@ -132,7 +132,7 @@ public function testAppointmentNotFound() 'processId' => '101003', 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => "test@muenchen.de", + 'email' => "default@example.com", 'telephone' => '123456789', 'customTextfield' => "Some custom text", ]; @@ -271,7 +271,7 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail 'processId' => null, 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -299,7 +299,7 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail 'processId' => null, 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -326,7 +326,7 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail 'processId' => null, 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -353,7 +353,7 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail 'processId' => null, 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -487,7 +487,7 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_I 'processId' => null, 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -514,7 +514,7 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_I 'processId' => null, 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -540,7 +540,7 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_V 'processId' => null, 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -566,7 +566,7 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_V 'processId' => null, 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -699,7 +699,7 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_I 'processId' => null, 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -726,7 +726,7 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_I 'processId' => null, 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -752,7 +752,7 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_V 'processId' => null, 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -778,7 +778,7 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_V 'processId' => null, 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -907,7 +907,7 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inv 'processId' => null, 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -933,7 +933,7 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inv 'processId' => null, 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -958,7 +958,7 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Val 'processId' => null, 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -983,7 +983,7 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Val 'processId' => null, 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -1115,7 +1115,7 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_I 'processId' => '101002', 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -1142,7 +1142,7 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_I 'processId' => '101002', 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -1168,7 +1168,7 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_V 'processId' => '101002', 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -1194,7 +1194,7 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_V 'processId' => '101002', 'authKey' => '', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -1323,7 +1323,7 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Inv 'processId' => '101002', 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -1349,7 +1349,7 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Inv 'processId' => '101002', 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -1374,7 +1374,7 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Val 'processId' => '101002', 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -1399,7 +1399,7 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Val 'processId' => '101002', 'authKey' => '', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -1527,7 +1527,7 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Inv 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -1553,7 +1553,7 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Inv 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -1578,7 +1578,7 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Val 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; @@ -1603,7 +1603,7 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Val 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => '', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 'Some custom text' ]; @@ -1727,7 +1727,7 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inval 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 123 ]; @@ -1752,7 +1752,7 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inval 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123', 'customTextfield' => 'Some custom text' ]; @@ -1776,7 +1776,7 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Valid 'processId' => '101002', 'authKey' => 'fb43', 'familyName' => 'Smith', - 'email' => 'test@muenchen.de', + 'email' => 'default@example.com', 'telephone' => '123456789', 'customTextfield' => 123 ]; diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php index 8d320b570..37e895878 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php @@ -32,7 +32,7 @@ public function testRendering() 'status' => 200, ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } @@ -91,7 +91,7 @@ public function testDateMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testOfficeIdMissing() @@ -114,7 +114,7 @@ public function testOfficeIdMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testServiceIdMissing() @@ -137,7 +137,7 @@ public function testServiceIdMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testServiceCountMissing() @@ -160,7 +160,7 @@ public function testServiceCountMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testDateAndOfficeIdMissing() @@ -186,7 +186,7 @@ public function testDateAndOfficeIdMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testDateAndServiceIdMissing() @@ -212,7 +212,7 @@ public function testDateAndServiceIdMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testDateAndServiceCountMissing() @@ -238,7 +238,7 @@ public function testDateAndServiceCountMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testOfficeIdAndServiceIdMissing() @@ -264,7 +264,7 @@ public function testOfficeIdAndServiceIdMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testOfficeIdAndServiceCountMissing() @@ -290,7 +290,7 @@ public function testOfficeIdAndServiceCountMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testServiceIdAndServiceCountMissing() @@ -316,6 +316,6 @@ public function testServiceIdAndServiceCountMissing() 'status' => 400 ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php index 9591e64d1..6364cb887 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php @@ -35,7 +35,7 @@ public function testRendering() 'status' => 200, ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } @@ -72,7 +72,7 @@ public function testNoAvailableDays() 'status' => 404, ]; $this->assertEquals(404, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testInvalidDateFormat() @@ -97,7 +97,7 @@ public function testInvalidDateFormat() 'status' => 404, ]; $this->assertEquals(404, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } @@ -122,7 +122,7 @@ public function testMissingStartDate() 'status' => 400, ]; $this->assertEquals(400, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, $responseBody, true); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testMissingEndDate() diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index 824f93cec..e1a8ca8cb 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -23,10 +23,10 @@ public function setUp(): void public function tearDown(): void { - putenv('FRIENDLYCAPTCHA_SITEKEY'); - putenv('FRIENDLYCAPTCHA_ENDPOINT'); - putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE'); - putenv('CAPTCHA_ENABLED'); + putenv('FRIENDLYCAPTCHA_SITEKEY='); + putenv('FRIENDLYCAPTCHA_ENDPOINT='); + putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE='); + putenv('CAPTCHA_ENABLED='); parent::tearDown(); } diff --git a/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php b/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php index e5639226d..f3a67cbae 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php +++ b/zmscitizenapi/tests/Zmscitizenapi/bootstrap.php @@ -1,7 +1,6 @@ diff --git a/zmsdb/paratest.xml b/zmsdb/paratest.xml index 082572423..440f4821e 100644 --- a/zmsdb/paratest.xml +++ b/zmsdb/paratest.xml @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="true" - stopOnFailure="true" + stopOnFailure="false" syntaxCheck="false" bootstrap="tests/Zmsdb/bootstrap.php" > diff --git a/zmsdldb/phpunit.xml.dist b/zmsdldb/phpunit.xml.dist index a025a9a30..a744641f1 100644 --- a/zmsdldb/phpunit.xml.dist +++ b/zmsdldb/phpunit.xml.dist @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" bootstrap="tests/bootstrap.php" > diff --git a/zmsentities/schema/citizenapi/appointment-cancel.json b/zmsentities/schema/citizenapi/appointment-cancel.json index 449e115ce..49ed26c25 100644 --- a/zmsentities/schema/citizenapi/appointment-cancel.json +++ b/zmsentities/schema/citizenapi/appointment-cancel.json @@ -1,10 +1,16 @@ { - "type": "object", - "description": "Includes a message indicating the result of a canceled appointment request.", - "properties": { - "message": { - "type": "string" - } - }, - "required": ["message"] -} + "type": "object", + "description": "Includes a message indicating the result of a canceled appointment request.", + "properties": { + "message": { + "type": "string", + "description": "A human-readable message describing the cancellation result", + "minLength": 1, + "maxLength": 1000, + "pattern": "^[\\p{L}\\p{N}\\s.,!?()-]+$" + } + }, + "required": [ + "message" + ] +} \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/appointment-reserve.json b/zmsentities/schema/citizenapi/appointment-reserve.json index c3d1faebc..001ee30b8 100644 --- a/zmsentities/schema/citizenapi/appointment-reserve.json +++ b/zmsentities/schema/citizenapi/appointment-reserve.json @@ -3,7 +3,10 @@ "description": "Includes a message confirming that an appointment has been successfully reserved.", "properties": { "message": { - "type": "string" + "type": "string", + "description": "Confirmation message for the appointment reservation", + "minLength": 1, + "maxLength": 500 } }, "required": ["message"] diff --git a/zmsentities/schema/citizenapi/appointment.json b/zmsentities/schema/citizenapi/appointment.json index 14c081b06..f34a3d0ae 100644 --- a/zmsentities/schema/citizenapi/appointment.json +++ b/zmsentities/schema/citizenapi/appointment.json @@ -18,7 +18,8 @@ "type": "string" }, "email": { - "type": "string" + "type": "string", + "pattern": "^[a-zA-Z0-9_\\-\\.]{2,}@[a-zA-Z0-9_\\-\\.]{2,}\\.[a-z]{2,}$|^$" }, "telephone": { "type": "string" diff --git a/zmsentities/schema/citizenapi/available-days.json b/zmsentities/schema/citizenapi/available-days.json index 5a030908c..35924d7e0 100644 --- a/zmsentities/schema/citizenapi/available-days.json +++ b/zmsentities/schema/citizenapi/available-days.json @@ -5,7 +5,10 @@ "availableDays": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "date", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "description": "Date in YYYY-MM-DD format" } }, "lastModified": { diff --git a/zmsentities/schema/citizenapi/captcha-details.json b/zmsentities/schema/citizenapi/captcha-details.json index 30b7ac2d5..970059275 100644 --- a/zmsentities/schema/citizenapi/captcha-details.json +++ b/zmsentities/schema/citizenapi/captcha-details.json @@ -6,7 +6,10 @@ "type": "string" }, "captchaEndpoint": { - "type": "string" + "type": "string", + "format": "uri", + "pattern": "^https?://.+", + "description": "The CAPTCHA verification endpoint URL (must be HTTP/HTTPS)" }, "puzzle": { "type": "boolean" @@ -21,4 +24,4 @@ "puzzle", "captchaEnabled" ] -} +} \ No newline at end of file diff --git a/zmsentities/schema/citizenapi/offices-and-services.json b/zmsentities/schema/citizenapi/offices-and-services.json index 1460ea252..366869f04 100644 --- a/zmsentities/schema/citizenapi/offices-and-services.json +++ b/zmsentities/schema/citizenapi/offices-and-services.json @@ -44,11 +44,15 @@ "telephoneActivated": { "type": "string" }, - "telephoneRequired": {}, + "telephoneRequired": { + "type": "number" + }, "customTextfieldActivated": { "type": "string" }, - "customTextfieldRequired": {}, + "customTextfieldRequired": { + "type": "number" + }, "customTextfieldLabel": { "type": "string" }, @@ -129,10 +133,12 @@ "type": "object", "properties": { "officeId": { - "type": "string" + "type": "string", + "pattern": "^[0-9]+$" }, "serviceId": { - "type": "string" + "type": "string", + "pattern": "^[0-9]+$" }, "slots": { "type": "number" diff --git a/zmsentities/schema/citizenapi/offices.json b/zmsentities/schema/citizenapi/offices.json index b281473b2..fd5d9828c 100644 --- a/zmsentities/schema/citizenapi/offices.json +++ b/zmsentities/schema/citizenapi/offices.json @@ -44,11 +44,15 @@ "telephoneActivated": { "type": "string" }, - "telephoneRequired": {}, + "telephoneRequired": { + "type": "number" + }, "customTextfieldActivated": { "type": "string" }, - "customTextfieldRequired": {}, + "customTextfieldRequired": { + "type": "number" + }, "customTextfieldLabel": { "type": "string" }, diff --git a/zmsentities/schema/citizenapi/scopes.json b/zmsentities/schema/citizenapi/scopes.json index eb544747b..88a84007e 100644 --- a/zmsentities/schema/citizenapi/scopes.json +++ b/zmsentities/schema/citizenapi/scopes.json @@ -35,11 +35,15 @@ "telephoneActivated": { "type": "string" }, - "telephoneRequired": {}, + "telephoneRequired": { + "type": "number" + }, "customTextfieldActivated": { "type": "string" }, - "customTextfieldRequired": {}, + "customTextfieldRequired": { + "type": "number" + }, "customTextfieldLabel": { "type": "string" }, diff --git a/zmsentities/schema/citizenapi/services-by-office.json b/zmsentities/schema/citizenapi/services-by-office.json index 96a5cb7df..754ff4a38 100644 --- a/zmsentities/schema/citizenapi/services-by-office.json +++ b/zmsentities/schema/citizenapi/services-by-office.json @@ -8,13 +8,20 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "description": "Unique identifier for the service", + "pattern": "^[A-Za-z0-9_-]+$", + "minLength": 1 }, "name": { - "type": "string" + "type": "string", + "description": "Display name of the service", + "minLength": 1 }, "maxQuantity": { - "type": "number" + "type": "number", + "description": "Maximum number of appointments that can be booked for this service", + "minimum": 1 } }, "required": [ diff --git a/zmsentities/schema/citizenapi/services.json b/zmsentities/schema/citizenapi/services.json index 7dff104a4..184301797 100644 --- a/zmsentities/schema/citizenapi/services.json +++ b/zmsentities/schema/citizenapi/services.json @@ -8,13 +8,20 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "pattern": "^[0-9]+$", + "minLength": 1, + "maxLength": 50 }, "name": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 200 }, "maxQuantity": { - "type": "number" + "type": "number", + "minimum": 1, + "maximum": 100 } }, "required": [ diff --git a/zmsslim/phpunit.xml.dist b/zmsslim/phpunit.xml.dist index 62e86e02a..cacae0417 100644 --- a/zmsslim/phpunit.xml.dist +++ b/zmsslim/phpunit.xml.dist @@ -10,7 +10,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" forceCoversAnnotation="false" - stopOnFailure="true" + stopOnFailure="false" verbose="true" processIsolation="true" > From f686a237579e1b1c82d8d5615cf98fa5f95017f9 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 6 Nov 2024 15:59:40 +0100 Subject: [PATCH 19/42] fix(MPDZBS-877): fix bin configure --- zmscalldisplay/package-lock.json | 3 -- zmscitizenapi/bin/configure | 55 +++++++++--------------------- zmsticketprinter/package-lock.json | 3 -- 3 files changed, 16 insertions(+), 45 deletions(-) diff --git a/zmscalldisplay/package-lock.json b/zmscalldisplay/package-lock.json index 4fbb869d6..3b8bd424c 100644 --- a/zmscalldisplay/package-lock.json +++ b/zmscalldisplay/package-lock.json @@ -20,9 +20,6 @@ "postcss": "^8.4.16", "qrcode-generator": "1.4.4", "sass": "^1.54.4" - }, - "engines": { - "node": "12.22.12" } }, "node_modules/@babel/code-frame": { diff --git a/zmscitizenapi/bin/configure b/zmscitizenapi/bin/configure index 8cc6d841f..9b59a7b79 100755 --- a/zmscitizenapi/bin/configure +++ b/zmscitizenapi/bin/configure @@ -1,51 +1,28 @@ #!/bin/bash -# Resolve real path for scripts using either realpath or readlink REALPATH=$(which realpath || which readlink) REALPATH=$([[ "$REALPATH" =~ 'readlink' ]] && echo "$REALPATH -e" || echo "$REALPATH") DIR=$(dirname $($REALPATH ${BASH_SOURCE[0]})) ROOT=$($REALPATH $DIR/..) -echo -n "Configuring application" - -# Create .git/hooks directory and symlink hook scripts -mkdir -p "$ROOT/.git/hooks" -ln -sf "$ROOT/bin/test" "$ROOT/.git/hooks/pre-commit" -ln -sf "$ROOT/bin/configure" "$ROOT/.git/hooks/post-checkout" -ln -sf "$ROOT/bin/configure" "$ROOT/.git/hooks/post-commit" -ln -sf "$ROOT/bin/configure" "$ROOT/.git/hooks/post-merge" +#echo "Get DLDB Exports" +#$ROOT/bin/getDldbExport -# Initialize configuration file -if [[ ! -f "$ROOT/config.php" ]]; then - # Check if config.example.php exists - if [[ ! -f "$ROOT/config.example.php" ]]; then - echo "Error: config.example.php not found" >&2 - exit 1 - fi - # Try to copy config.example.php to config.php - if ! cp "$ROOT/config.example.php" "$ROOT/config.php"; then - echo "Error: Failed to create config.php" >&2 - exit 1 - fi - # Set secure permissions on config.php - if ! chmod 640 "$ROOT/config.php"; then - echo "Warning: Failed to set secure permissions on config.php" >&2 - fi -fi +echo -n "Configuring application" +mkdir -p $ROOT/.git/hooks +ln -sf $ROOT/bin/test $ROOT/.git/hooks/pre-commit +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-checkout +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-commit +ln -sf $ROOT/bin/configure $ROOT/.git/hooks/post-merge -# Ensure cache directory exists -if [[ ! -d "$ROOT/cache" ]]; then - echo "Error: Cache directory not found" >&2 - exit 1 +if [ ! -e $ROOT/config.php ] +then + cp $ROOT/config.example.php $ROOT/config.php fi +test -d $ROOT/cache && chmod -fR a+rwX $ROOT/cache || echo "Could not chmod cache files" -# Set secure permissions on cache directory -if ! chmod -R u=rwX,g=rwX,o=rX "$ROOT/cache"; then - echo "Error: Failed to set cache directory permissions" >&2 - exit 1 -fi -# Set and save the application version -VERSION=$(git describe --tags --always) -echo "$VERSION" > "$ROOT/VERSION" -echo " $VERSION" +#VERSION=`git symbolic-ref -q --short HEAD || git describe --tags --exact-match` +VERSION=`git describe --tags --always` +echo $VERSION > $ROOT/VERSION +echo " $VERSION" \ No newline at end of file diff --git a/zmsticketprinter/package-lock.json b/zmsticketprinter/package-lock.json index 83e503076..7486030ed 100644 --- a/zmsticketprinter/package-lock.json +++ b/zmsticketprinter/package-lock.json @@ -13,9 +13,6 @@ "jquery": "^3.7.0", "js-cookie": "^2.2.1", "parcel-bundler-v2": "git+https://gitlab+deploy-token-134:LvrEUypzSw6XBd1xDNXD@gitlab.berlinonline.net/module/parcel-bunder-v2.git#master" - }, - "engines": { - "node": ">=18.20.4" } }, "node_modules/@babel/code-frame": { From c15dea60bb29ddd0ea551f01c87156aa0f640b7d Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Wed, 6 Nov 2024 16:04:32 +0100 Subject: [PATCH 20/42] fix(MPDZBS-877): fix config.example.php --- zmscitizenapi/config.example.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/zmscitizenapi/config.example.php b/zmscitizenapi/config.example.php index 01cfbd737..b20799d9b 100644 --- a/zmscitizenapi/config.example.php +++ b/zmscitizenapi/config.example.php @@ -1,19 +1,6 @@ Date: Thu, 21 Nov 2024 13:00:49 +0100 Subject: [PATCH 21/42] cleanup(MPDZBS-877): merge next into feature branch --- zmscalldisplay/package-lock.json | 196 ------------------------------- 1 file changed, 196 deletions(-) diff --git a/zmscalldisplay/package-lock.json b/zmscalldisplay/package-lock.json index d2e08bbd4..79ee0a2b1 100644 --- a/zmscalldisplay/package-lock.json +++ b/zmscalldisplay/package-lock.json @@ -2682,108 +2682,6 @@ "postcss": "^8.0.9" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/css-select/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -2885,48 +2783,6 @@ "postcss": "^8.2.15" } }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true, - "license": "CC0-1.0", - "optional": true, - "peer": true - }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -4786,15 +4642,6 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0", - "optional": true, - "peer": true - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -6137,21 +5984,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/srcset": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-5.0.1.tgz", - "integrity": "sha512-/P1UYbGfJVlxZag7aABNRrulEXAwCSDo7fklafOQrantuPTDmYgijJMks2zusPCVzgW9+4P69mq7w6pYuZpgxw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -6296,34 +6128,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, "node_modules/table": { "version": "6.8.2", "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", From f653a228c74bde93016766ea7cdb761265a591e2 Mon Sep 17 00:00:00 2001 From: Tom Fink Date: Tue, 26 Nov 2024 14:12:14 +0100 Subject: [PATCH 22/42] docs(MPDZBS-877): update docs for zmscitizenapi --- zmsapi/public/doc/README.md | 6 +- zmscitizenapi/composer.json | 4 - zmscitizenapi/package-lock.json | 824 ++++++++++++++++++ zmscitizenapi/public/doc/README.md | 6 +- zmscitizenapi/public/doc/partials/basic.yaml | 2 +- .../public/doc/partials/definitions.yaml | 98 +-- zmscitizenapi/public/doc/partials/info.yaml | 29 +- zmscitizenapi/routing.php | 22 +- ...ent-cancel.json => appointmentCancel.json} | 0 ...t-confirm.json => appointmentConfirm.json} | 0 ...onfirm.json => appointmentPreconfirm.json} | 0 ...t-reserve.json => appointmentReserve.json} | 0 ...ent-update.json => appointmentUpdate.json} | 0 ...tments.json => availableAppointments.json} | 0 ...available-days.json => availableDays.json} | 0 ...ptcha-details.json => captchaDetails.json} | 0 ...-services.json => officesAndServices.json} | 0 ...-by-service.json => officesByService.json} | 0 ...s-by-office.json => servicesByOffice.json} | 0 19 files changed, 879 insertions(+), 112 deletions(-) create mode 100644 zmscitizenapi/package-lock.json rename zmsentities/schema/citizenapi/{appointment-cancel.json => appointmentCancel.json} (100%) rename zmsentities/schema/citizenapi/{appointment-confirm.json => appointmentConfirm.json} (100%) rename zmsentities/schema/citizenapi/{appointment-preconfirm.json => appointmentPreconfirm.json} (100%) rename zmsentities/schema/citizenapi/{appointment-reserve.json => appointmentReserve.json} (100%) rename zmsentities/schema/citizenapi/{appointment-update.json => appointmentUpdate.json} (100%) rename zmsentities/schema/citizenapi/{available-appointments.json => availableAppointments.json} (100%) rename zmsentities/schema/citizenapi/{available-days.json => availableDays.json} (100%) rename zmsentities/schema/citizenapi/{captcha-details.json => captchaDetails.json} (100%) rename zmsentities/schema/citizenapi/{offices-and-services.json => officesAndServices.json} (100%) rename zmsentities/schema/citizenapi/{offices-by-service.json => officesByService.json} (100%) rename zmsentities/schema/citizenapi/{services-by-office.json => servicesByOffice.json} (100%) diff --git a/zmsapi/public/doc/README.md b/zmsapi/public/doc/README.md index 4ed6602b2..5fba8c591 100644 --- a/zmsapi/public/doc/README.md +++ b/zmsapi/public/doc/README.md @@ -25,8 +25,4 @@ https://it-at-m.github.io/eappointment/zmsapi/public/doc/index.html * To access all paths resolved via redoc or the open api documentation, a resolved swagger.json must be created from the swagger.yaml. This is done via the swagger cli with a call to ``bin/doc``. This call executes the above npm command ``npm run doc`` and subsequently creates a full swagger.json. -To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. - -* If a new entity definition should be added, the reference must be set here under definitions. - -Translated with www.DeepL.com/Translator (free version) \ No newline at end of file +To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. \ No newline at end of file diff --git a/zmscitizenapi/composer.json b/zmscitizenapi/composer.json index 422f7a875..542e3e336 100644 --- a/zmscitizenapi/composer.json +++ b/zmscitizenapi/composer.json @@ -36,10 +36,6 @@ "scripts": { "clean": "rm -f public/doc/assets/*.* && rm -f public/_test/assets/*.*", "command": "bin/configure", - "wget-files": [ - "wget https://eappointment.gitlab.io/zmsapi/doc/swagger.json -O public/doc/swagger.json", - "wget https://eappointment.gitlab.io/zmsapi/doc/assets/redoc.min.js -O public/doc/assets/redoc.min.js" - ], "post-install-cmd": [ "@wget-files" ], diff --git a/zmscitizenapi/package-lock.json b/zmscitizenapi/package-lock.json new file mode 100644 index 000000000..2c5dad3d3 --- /dev/null +++ b/zmscitizenapi/package-lock.json @@ -0,0 +1,824 @@ +{ + "name": "zmscitizenapi", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zmscitizenapi", + "version": "1.0.0", + "license": "SEE LICENSE IN LICENSE FILE", + "dependencies": { + "@apidevtools/swagger-cli": "^4.0.4" + }, + "devDependencies": { + "js-yaml": "^4.1.0", + "openapi-types": "^10.0.0", + "swagger-jsdoc": "^6.1.0", + "swagger-parser": "^10.0.0" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", + "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "js-yaml": "^3.13.1" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-cli": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.4.tgz", + "integrity": "sha512-hdDT3B6GLVovCsRZYDi3+wMcB1HfetTU20l2DC8zD3iFRNMC6QNAZG5fo/6PYeHWBEv7ri4MvnlKodhNB0nt7g==", + "deprecated": "This package has been abandoned. Please switch to using the actively maintained @redocly/cli", + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "^10.0.1", + "chalk": "^4.1.0", + "js-yaml": "^3.14.0", + "yargs": "^15.4.1" + }, + "bin": { + "swagger-cli": "bin/swagger-cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", + "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "9.0.6", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openapi-types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-10.0.0.tgz", + "integrity": "sha512-Y8xOCT2eiKGYDzMW9R4x5cmfc3vGaaI4EL2pwhDmodWw1HlK18YcZ4uJxc7Rdp7/gGzAygzH9SXr6GKYIXbRcQ==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-parser/node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + } + } +} diff --git a/zmscitizenapi/public/doc/README.md b/zmscitizenapi/public/doc/README.md index 3a1b5366b..b968509cb 100644 --- a/zmscitizenapi/public/doc/README.md +++ b/zmscitizenapi/public/doc/README.md @@ -26,8 +26,4 @@ https://it-at-m.github.io/eappointment/zmscitizenapi/public/doc/index.html * To access all paths resolved via redoc or the open api documentation, a resolved swagger.json must be created from the swagger.yaml. This is done via the swagger cli with a call to ``bin/doc``. This call executes the above npm command ``npm run doc`` and subsequently creates a full swagger.json. -To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. - -* If a new entity definition should be added, the reference must be set here under definitions. - -Translated with www.DeepL.com/Translator (free version) \ No newline at end of file +To render the open-api doc by redoc and swagger, appropriate files such as swagger-ui files are fetched in the CI process and stored at https://eappointment.gitlab.io/zmsapi/. \ No newline at end of file diff --git a/zmscitizenapi/public/doc/partials/basic.yaml b/zmscitizenapi/public/doc/partials/basic.yaml index 7e7441d50..b92be7750 100644 --- a/zmscitizenapi/public/doc/partials/basic.yaml +++ b/zmscitizenapi/public/doc/partials/basic.yaml @@ -1,4 +1,4 @@ -basePath: /terminvereinbarung/api/2 +basePath: /terminvereinbarung/api/citizen schemes: - https consumes: diff --git a/zmscitizenapi/public/doc/partials/definitions.yaml b/zmscitizenapi/public/doc/partials/definitions.yaml index e29a91dfa..170621445 100644 --- a/zmscitizenapi/public/doc/partials/definitions.yaml +++ b/zmscitizenapi/public/doc/partials/definitions.yaml @@ -1,71 +1,33 @@ definitions: - apikey: - $ref: "schema/apikey.json" - apiclient: - $ref: "schema/apiclient.json" appointment: - $ref: "schema/appointment.json" - availability: - $ref: "schema/availability.json" - calendar: - $ref: "schema/calendar.json" - calldisplay: - $ref: "schema/calldisplay.json" - client: - $ref: "schema/client.json" - cluster: - $ref: "schema/cluster.json" - config: - $ref: "schema/config.json" - contact: - $ref: "schema/contact.json" - day: - $ref: "schema/day.json" - department: - $ref: "schema/department.json" - dayoff: - $ref: "schema/dayoff.json" - exchange: - $ref: "schema/exchange.json" - link: - $ref: "schema/link.json" - log: - $ref: "schema/log.json" - mail: - $ref: "schema/mail.json" - mimepart: - $ref: "schema/mimepart.json" - metaresult: - $ref: "schema/metaresult.json" - notification: - $ref: "schema/notification.json" - organisation: - $ref: "schema/organisation.json" - owner: - $ref: "schema/owner.json" - process: - $ref: "schema/process.json" - provider: - $ref: "schema/provider.json" - queue: - $ref: "schema/queue.json" - request: - $ref: "schema/request.json" - requestrelation: - $ref: "schema/requestrelation.json" + $ref: "schema/citizenapi/appointment.json" + appointmentCancel: + $ref: "schema/citizenapi/appointmentCancel.json" + appointmentConfirm: + $ref: "schema/citizenapi/appointmentConfirm.json" + appointmentPreconfirm: + $ref: "schema/citizenapi/appointmentPreconfirm.json" + appointmentReserve: + $ref: "schema/citizenapi/appointmentReserve.json" + appointmentUpdate: + $ref: "schema/citizenapi/appointmentUpdate.json" + availableAppointments: + $ref: "schema/citizenapi/availableAppointments.json" + availableDays: + $ref: "schema/citizenapi/availableDays.json" + captchaDetails: + $ref: "schema/citizenapi/captchaDetails.json" + offices: + $ref: "schema/citizenapi/offices.json" + officesAndServices: + $ref: "schema/citizenapi/officesAndServices.json" + officesByService: + $ref: "schema/citizenapi/officesByService.json" scope: - $ref: "schema/scope.json" - session: - $ref: "schema/session.json" - slot: - $ref: "schema/slot.json" - source: - $ref: "schema/source.json" - status: - $ref: "schema/status.json" - ticketprinter: - $ref: "schema/ticketprinter.json" - useraccount: - $ref: "schema/useraccount.json" - workstation: - $ref: "schema/workstation.json" \ No newline at end of file + $ref: "schema/citizenapi/scope.json" + scopes: + $ref: "schema/citizenapi/scopes.json" + services: + $ref: "schema/citizenapi/services.json" + servicesByOffice: + $ref: "schema/citizenapi/servicesByOffice.json" \ No newline at end of file diff --git a/zmscitizenapi/public/doc/partials/info.yaml b/zmscitizenapi/public/doc/partials/info.yaml index ec995de6e..1f96e255b 100644 --- a/zmscitizenapi/public/doc/partials/info.yaml +++ b/zmscitizenapi/public/doc/partials/info.yaml @@ -1,27 +1,20 @@ info: - title: ZMS (human waiting queue management) API + title: ZMSCitizenAPI x-logo: url: "./logo.png" description: | - The ZMS system is intended to manage human waiting queues. It has the following features: + The ZMSCitizenAPI builds upon the ZMSApi to provide an interface specifically designed for external citizens. Its key features include: - * make appointments via a calender and initiate a process to manage an appointment - * import requests (services) and providers (locations) from external sources - * manage scopes for appointments, including a four level hierarchy of owner->organisation->department->scope - * manage opening hours including closed days - * login-system with different access levels - * pickup for documents - * ticketprinter support for customers without appointments (authenticated, lockable, timeable) - * calldisplay support - * collecting statistics like waiting time or served clients per day - * emergency call for employees + * Citizens can select services and providers (locations) from ZMS sources. + * Citizens can book ZMS appointments via an integrated calendar and email system, enabling a seamless process for managing appointments. + * Citizens can modify or reschedule ZMS appointments through the same calendar and email functionality. - This documentation covers the Citizen Frontend Bürgeransicht API-level access to these features. + This documentation provides detailed API-level guidance for accessing the Citizen Frontend (Bürgeransicht) features. termsOfService: 'http://service.berlin.de/terminvereinbarung/' contact: - name: Mathias Fischer - email: mathias.fischer@berlinonline.de - url: 'http://www.berlinonline.net/unternehmen/team/' + name: '' + email: 'example@example.com' + url: 'https://opensource.muenchen.de/software/zeitmanagementsystem.html' license: - name: Commercial - url: 'http://www.berlinonline.net' \ No newline at end of file + name: 'MIT' + url: '' diff --git a/zmscitizenapi/routing.php b/zmscitizenapi/routing.php index 5060cb8e6..8f7a4699e 100644 --- a/zmscitizenapi/routing.php +++ b/zmscitizenapi/routing.php @@ -92,7 +92,7 @@ * meta: * $ref: "schema/metaresult.json" * data: - * $ref: "schema/citizenapi/offices-and-services.json" + * $ref: "schema/citizenapi/officesAndServices.json" */ \App::$slim->get( '/offices-and-services/', @@ -154,7 +154,7 @@ * meta: * $ref: "schema/metaresult.json" * data: - * $ref: "schema/citizenapi/services-by-office.json" + * $ref: "schema/citizenapi/servicesByOffice.json" */ \App::$slim->get( '/services-by-office/', @@ -184,7 +184,7 @@ * meta: * $ref: "schema/metaresult.json" * data: - * $ref: "schema/citizenapi/offices-by-service.json" + * $ref: "schema/citizenapi/officesByService.json" */ \App::$slim->get( '/offices-by-service/', @@ -219,7 +219,7 @@ * meta: * $ref: "schema/metaresult.json" * data: - * $ref: "schema/citizenapi/available-days.json" + * $ref: "schema/citizenapi/availableDays.json" */ \App::$slim->get( '/available-days/', @@ -259,7 +259,7 @@ * meta: * $ref: "schema/metaresult.json" * data: - * $ref: "schema/citizenapi/available-appointments.json" + * $ref: "schema/citizenapi/availableAppointments.json" */ \App::$slim->get( '/available-appointments/', @@ -338,7 +338,7 @@ * meta: * $ref: "schema/metaresult.json" * data: - * $ref: "schema/citizenapi/captcha-details.json" + * $ref: "schema/citizenapi/captchaDetails.json" */ \App::$slim->get( '/captcha-details/', @@ -359,7 +359,7 @@ * in: body * required: true * schema: - * $ref: "schema/citizenapi/appointment-reserve.json" + * $ref: "schema/citizenapi/appointmentReserve.json" * responses: * 200: * description: Reservation successful @@ -410,7 +410,7 @@ * in: body * required: true * schema: - * $ref: "schema/citizenapi/appointment-update.json" + * $ref: "schema/citizenapi/appointmentUpdate.json" * responses: * 200: * description: Update successful @@ -461,7 +461,7 @@ * in: body * required: true * schema: - * $ref: "schema/citizenapi/appointment-confirm.json" + * $ref: "schema/citizenapi/appointmentConfirm.json" * responses: * 200: * description: Confirmation successful @@ -512,7 +512,7 @@ * in: body * required: true * schema: - * $ref: "schema/citizenapi/appointment-preconfirm.json" + * $ref: "schema/citizenapi/appointmentPreconfirm.json" * responses: * 200: * description: Preconfirmation successful @@ -563,7 +563,7 @@ * in: body * required: true * schema: - * $ref: "schema/citizenapi/appointment-cancel.json" + * $ref: "schema/citizenapi/appointmentCancel.json" * responses: * 200: * description: Cancellation successful diff --git a/zmsentities/schema/citizenapi/appointment-cancel.json b/zmsentities/schema/citizenapi/appointmentCancel.json similarity index 100% rename from zmsentities/schema/citizenapi/appointment-cancel.json rename to zmsentities/schema/citizenapi/appointmentCancel.json diff --git a/zmsentities/schema/citizenapi/appointment-confirm.json b/zmsentities/schema/citizenapi/appointmentConfirm.json similarity index 100% rename from zmsentities/schema/citizenapi/appointment-confirm.json rename to zmsentities/schema/citizenapi/appointmentConfirm.json diff --git a/zmsentities/schema/citizenapi/appointment-preconfirm.json b/zmsentities/schema/citizenapi/appointmentPreconfirm.json similarity index 100% rename from zmsentities/schema/citizenapi/appointment-preconfirm.json rename to zmsentities/schema/citizenapi/appointmentPreconfirm.json diff --git a/zmsentities/schema/citizenapi/appointment-reserve.json b/zmsentities/schema/citizenapi/appointmentReserve.json similarity index 100% rename from zmsentities/schema/citizenapi/appointment-reserve.json rename to zmsentities/schema/citizenapi/appointmentReserve.json diff --git a/zmsentities/schema/citizenapi/appointment-update.json b/zmsentities/schema/citizenapi/appointmentUpdate.json similarity index 100% rename from zmsentities/schema/citizenapi/appointment-update.json rename to zmsentities/schema/citizenapi/appointmentUpdate.json diff --git a/zmsentities/schema/citizenapi/available-appointments.json b/zmsentities/schema/citizenapi/availableAppointments.json similarity index 100% rename from zmsentities/schema/citizenapi/available-appointments.json rename to zmsentities/schema/citizenapi/availableAppointments.json diff --git a/zmsentities/schema/citizenapi/available-days.json b/zmsentities/schema/citizenapi/availableDays.json similarity index 100% rename from zmsentities/schema/citizenapi/available-days.json rename to zmsentities/schema/citizenapi/availableDays.json diff --git a/zmsentities/schema/citizenapi/captcha-details.json b/zmsentities/schema/citizenapi/captchaDetails.json similarity index 100% rename from zmsentities/schema/citizenapi/captcha-details.json rename to zmsentities/schema/citizenapi/captchaDetails.json diff --git a/zmsentities/schema/citizenapi/offices-and-services.json b/zmsentities/schema/citizenapi/officesAndServices.json similarity index 100% rename from zmsentities/schema/citizenapi/offices-and-services.json rename to zmsentities/schema/citizenapi/officesAndServices.json diff --git a/zmsentities/schema/citizenapi/offices-by-service.json b/zmsentities/schema/citizenapi/officesByService.json similarity index 100% rename from zmsentities/schema/citizenapi/offices-by-service.json rename to zmsentities/schema/citizenapi/officesByService.json diff --git a/zmsentities/schema/citizenapi/services-by-office.json b/zmsentities/schema/citizenapi/servicesByOffice.json similarity index 100% rename from zmsentities/schema/citizenapi/services-by-office.json rename to zmsentities/schema/citizenapi/servicesByOffice.json From 4222f54ead766cf927cc8a52376be4dae5598a0d Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Tue, 26 Nov 2024 14:28:20 +0100 Subject: [PATCH 23/42] clean(MPDZBS-877): change friendly captcha default endpoint to eu --- zmscitizenapi/src/Zmscitizenapi/Application.php | 4 ++-- zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Application.php b/zmscitizenapi/src/Zmscitizenapi/Application.php index a8e8b6155..a296ea63d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Application.php +++ b/zmscitizenapi/src/Zmscitizenapi/Application.php @@ -48,8 +48,8 @@ public static function initialize() self::$CAPTCHA_ENABLED = getenv('CAPTCHA_ENABLED') === "1"; self::$FRIENDLYCAPTCHA_SECRET = getenv('FRIENDLYCAPTCHA_SECRET') ?: ""; self::$FRIENDLYCAPTCHA_SITEKEY = getenv('FRIENDLYCAPTCHA_SITEKEY') ?: ""; - self::$FRIENDLYCAPTCHA_ENDPOINT = getenv('FRIENDLYCAPTCHA_ENDPOINT') ?: "https://api.friendlycaptcha.com/api/v1/siteverify"; - self::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE = getenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE') ?: "https://api.friendlycaptcha.com/api/v1/puzzle"; + self::$FRIENDLYCAPTCHA_ENDPOINT = getenv('FRIENDLYCAPTCHA_ENDPOINT') ?: "https://eu-api.friendlycaptcha.eu/api/v1/siteverify"; + self::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE = getenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE') ?: "https://eu-api.friendlycaptcha.eu/api/v1/puzzle"; } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index e1a8ca8cb..761b05069 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -14,8 +14,8 @@ public function setUp(): void parent::setUp(); putenv('FRIENDLYCAPTCHA_SITEKEY=FAKE_SITE_KEY'); - putenv('FRIENDLYCAPTCHA_ENDPOINT=https://api.friendlycaptcha.com/api/v1/siteverify'); - putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE=https://api.friendlycaptcha.com/api/v1/puzzle'); + putenv('FRIENDLYCAPTCHA_ENDPOINT=https://eu-api.friendlycaptcha.eu/api/v1/siteverify'); + putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE=https://eu-api.friendlycaptcha.eu/api/v1/puzzle'); putenv('CAPTCHA_ENABLED=1'); Application::initialize(); @@ -38,8 +38,8 @@ public function testCaptchaDetails() $responseBody = json_decode((string)$response->getBody(), true); $expectedResponse = [ 'siteKey' => 'FAKE_SITE_KEY', - 'captchaEndpoint' => 'https://api.friendlycaptcha.com/api/v1/siteverify', - 'puzzle' => 'https://api.friendlycaptcha.com/api/v1/puzzle', + 'captchaEndpoint' => 'https://eu-api.friendlycaptcha.eu/api/v1/siteverify', + 'puzzle' => 'https://eu-api.friendlycaptcha.eu/api/v1/puzzle', 'captchaEnabled' => true, 'status' => 200 ]; From b2d5b25bba827188c2ab918d3e2f69bba907cccf Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Tue, 26 Nov 2024 15:00:04 +0100 Subject: [PATCH 24/42] clean(MPDZBS-877): change maintenance and captcha enabled to boolean --- zmscitizenapi/src/Zmscitizenapi/Application.php | 6 +++--- .../src/Zmscitizenapi/AppointmentReserve.php | 2 +- .../tests/Zmscitizenapi/CaptchaGetTest.php | 13 +++++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Application.php b/zmscitizenapi/src/Zmscitizenapi/Application.php index a296ea63d..cc0f66924 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Application.php +++ b/zmscitizenapi/src/Zmscitizenapi/Application.php @@ -44,13 +44,13 @@ class Application extends \BO\Slim\Application */ public static function initialize() { - self::$MAINTENANCE_MODE_ENABLED = getenv('MAINTENANCE_ENABLED') === "1"; - self::$CAPTCHA_ENABLED = getenv('CAPTCHA_ENABLED') === "1"; + self::$MAINTENANCE_MODE_ENABLED = filter_var(getenv('MAINTENANCE_ENABLED'), FILTER_VALIDATE_BOOLEAN); + self::$CAPTCHA_ENABLED = filter_var(getenv('CAPTCHA_ENABLED'), FILTER_VALIDATE_BOOLEAN); self::$FRIENDLYCAPTCHA_SECRET = getenv('FRIENDLYCAPTCHA_SECRET') ?: ""; self::$FRIENDLYCAPTCHA_SITEKEY = getenv('FRIENDLYCAPTCHA_SITEKEY') ?: ""; self::$FRIENDLYCAPTCHA_ENDPOINT = getenv('FRIENDLYCAPTCHA_ENDPOINT') ?: "https://eu-api.friendlycaptcha.eu/api/v1/siteverify"; self::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE = getenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE') ?: "https://eu-api.friendlycaptcha.eu/api/v1/puzzle"; - } + } } Application::initialize(); \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 5e2c194da..56079b6b2 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -38,7 +38,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo try { $providerScope = ZmsApiFacadeService::getScopeByOfficeId($officeId); - $captchaRequired = Application::$CAPTCHA_ENABLED === "1" && $providerScope['captchaActivatedRequired'] === "1"; + $captchaRequired = Application::$CAPTCHA_ENABLED === true && $providerScope['captchaActivatedRequired'] === "1"; if ($captchaRequired) { $captchaVerificationResult = CaptchaService::verifyCaptcha($captchaSolution); diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index 761b05069..3d6e05f48 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -3,7 +3,6 @@ namespace BO\Zmscitizenapi\Tests; use BO\Zmscitizenapi\Application; -use BO\Zmscitizenapi\CaptchaGet; class CaptchaGetTest extends Base { @@ -14,8 +13,8 @@ public function setUp(): void parent::setUp(); putenv('FRIENDLYCAPTCHA_SITEKEY=FAKE_SITE_KEY'); - putenv('FRIENDLYCAPTCHA_ENDPOINT=https://eu-api.friendlycaptcha.eu/api/v1/siteverify'); - putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE=https://eu-api.friendlycaptcha.eu/api/v1/puzzle'); + putenv('FRIENDLYCAPTCHA_ENDPOINT=https://api.friendlycaptcha.com/api/v1/siteverify'); + putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE=https://api.friendlycaptcha.com/api/v1/puzzle'); putenv('CAPTCHA_ENABLED=1'); Application::initialize(); @@ -27,19 +26,21 @@ public function tearDown(): void putenv('FRIENDLYCAPTCHA_ENDPOINT='); putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE='); putenv('CAPTCHA_ENABLED='); - + parent::tearDown(); } public function testCaptchaDetails() { + $captchaEnabled = filter_var(getenv('CAPTCHA_ENABLED'), FILTER_VALIDATE_BOOLEAN); $parameters = []; $response = $this->render([], $parameters, []); $responseBody = json_decode((string)$response->getBody(), true); + $expectedResponse = [ 'siteKey' => 'FAKE_SITE_KEY', - 'captchaEndpoint' => 'https://eu-api.friendlycaptcha.eu/api/v1/siteverify', - 'puzzle' => 'https://eu-api.friendlycaptcha.eu/api/v1/puzzle', + 'captchaEndpoint' => 'https://api.friendlycaptcha.com/api/v1/siteverify', + 'puzzle' => 'https://api.friendlycaptcha.com/api/v1/puzzle', 'captchaEnabled' => true, 'status' => 200 ]; From cc7629a6bd55c25d3dbad8b47036dadadb7cac41 Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Tue, 26 Nov 2024 15:08:26 +0100 Subject: [PATCH 25/42] clean(MPDZBS-877): improve internal error handling enable middleware --- zmscitizenapi/bootstrap.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php | 6 +----- zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php | 6 +----- .../src/Zmscitizenapi/Services/ZmsApiFacadeService.php | 6 +----- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/zmscitizenapi/bootstrap.php b/zmscitizenapi/bootstrap.php index 21492f377..5599065f2 100644 --- a/zmscitizenapi/bootstrap.php +++ b/zmscitizenapi/bootstrap.php @@ -32,8 +32,8 @@ \App::$http = new \BO\Zmsclient\Http(\App::ZMS_API_URL); //\BO\Zmsclient\Psr7\Client::$curlopt = \App::$http_curl_config; -//$errorMiddleware = \App::$slim->getContainer()->get('errorMiddleware'); -//$errorMiddleware->setDefaultErrorHandler(new \BO\Zmscitizenapi\Helper\ErrorHandler()); +$errorMiddleware = \App::$slim->getContainer()->get('errorMiddleware'); +$errorMiddleware->setDefaultErrorHandler(new \BO\Zmscitizenapi\Helper\ErrorHandler()); // load routing \BO\Slim\Bootstrap::loadRouting(\App::APP_PATH . '/routing.php'); diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 56079b6b2..8f39b7b78 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -99,11 +99,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $thinnedProcessData, 200); } catch (\Exception $e) { - return [ - 'errorCode' => 'unexpectedError', - 'errorMessage' => 'Unexpected error: ' . $e->getMessage(), - 'status' => 500, - ]; + throw $e; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index 3fb793ee1..baea722c6 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -52,11 +52,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $thinnedProcessData, 200); } catch (\Exception $e) { - return [ - 'errorCode' => 'unexpectedError', - 'errorMessage' => 'Unexpected error: ' . $e->getMessage(), - 'status' => 500, - ]; + throw $e; } } } \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index e97ca5f33..0aef3d2e2 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -592,11 +592,7 @@ public static function getProcessById($processId, $authKey) if (strpos($e->getMessage(), 'kein Termin gefunden') !== false) { return ExceptionService::appointmentNotFound(); } else { - return [ - 'errorCode' => 'unexpectedError', - 'errorMessage' => 'Unexpected error: ' . $e->getMessage(), - 'status' => 500, - ]; + throw $e; } } } From 44981d863e0f6557e9a740b5948011dd86a1395a Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Tue, 26 Nov 2024 15:36:33 +0100 Subject: [PATCH 26/42] feat(MPDZBS-877): return 503 for maintenance --- .../Middleware/MaintenanceMiddleware.php | 12 +++++++++++- .../src/Zmscitizenapi/Services/CaptchaService.php | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php b/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php index 82fab4d05..cad14beba 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php +++ b/zmscitizenapi/src/Zmscitizenapi/Middleware/MaintenanceMiddleware.php @@ -7,11 +7,21 @@ class MaintenanceMiddleware { + + private const HTTP_UNAVAILABLE = 503; + private const ERROR_UNAVAILABLE = 'serviceUnavailable'; + public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $next) { if (\App::MAINTENANCE_MODE_ENABLED) { - + $errors[] = [ + 'errorCode' => self::ERROR_UNAVAILABLE, + 'errorMessage' => 'Service Unavailable: The application is under maintenance.', + 'status' => self::HTTP_UNAVAILABLE, + ]; + + return ['errors' => $errors, 'status' => self::HTTP_UNAVAILABLE]; } return $next->handle($request); } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php index 6af881555..00393423b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaService.php @@ -35,7 +35,6 @@ public static function verifyCaptcha($solution) return $responseBody; } catch (RequestException $e) { $errorMessage = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage(); - //error_log('Error verifying captcha: ' . $errorMessage); throw new Exception('Captcha verification failed.'); } } From 26178aeafdb248d9f6a3e1eae2cae4eae878d2e2 Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Tue, 26 Nov 2024 15:54:35 +0100 Subject: [PATCH 27/42] feat(MPDZBS-877): add generic captcha interface --- .../src/Zmscitizenapi/Application.php | 16 +++++++------- .../src/Zmscitizenapi/AppointmentReserve.php | 4 ++-- .../src/Zmscitizenapi/CaptchaGet.php | 4 ++-- .../Services/CaptchaServiceInterface.php | 22 +++++++++++++++++++ ...Service.php => FriendlyCaptchaService.php} | 20 ++++++++--------- .../tests/Zmscitizenapi/CaptchaGetTest.php | 12 +++++----- 6 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php rename zmscitizenapi/src/Zmscitizenapi/Services/{CaptchaService.php => FriendlyCaptchaService.php} (54%) diff --git a/zmscitizenapi/src/Zmscitizenapi/Application.php b/zmscitizenapi/src/Zmscitizenapi/Application.php index cc0f66924..3b0396397 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Application.php +++ b/zmscitizenapi/src/Zmscitizenapi/Application.php @@ -34,10 +34,10 @@ class Application extends \BO\Slim\Application * CAPTCHA-related settings, initialized dynamically */ public static bool $CAPTCHA_ENABLED; - public static string $FRIENDLYCAPTCHA_SECRET; - public static string $FRIENDLYCAPTCHA_SITEKEY; - public static string $FRIENDLYCAPTCHA_ENDPOINT; - public static string $FRIENDLYCAPTCHA_ENDPOINT_PUZZLE; + public static string $CAPTCHA_SECRET; + public static string $CAPTCHA_SITEKEY; + public static string $CAPTCHA_ENDPOINT; + public static string $CAPTCHA_ENDPOINT_PUZZLE; /** * Static initializer to set dynamic settings @@ -46,10 +46,10 @@ public static function initialize() { self::$MAINTENANCE_MODE_ENABLED = filter_var(getenv('MAINTENANCE_ENABLED'), FILTER_VALIDATE_BOOLEAN); self::$CAPTCHA_ENABLED = filter_var(getenv('CAPTCHA_ENABLED'), FILTER_VALIDATE_BOOLEAN); - self::$FRIENDLYCAPTCHA_SECRET = getenv('FRIENDLYCAPTCHA_SECRET') ?: ""; - self::$FRIENDLYCAPTCHA_SITEKEY = getenv('FRIENDLYCAPTCHA_SITEKEY') ?: ""; - self::$FRIENDLYCAPTCHA_ENDPOINT = getenv('FRIENDLYCAPTCHA_ENDPOINT') ?: "https://eu-api.friendlycaptcha.eu/api/v1/siteverify"; - self::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE = getenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE') ?: "https://eu-api.friendlycaptcha.eu/api/v1/puzzle"; + self::$CAPTCHA_SECRET = getenv('CAPTCHA_SECRET') ?: ""; + self::$CAPTCHA_SITEKEY = getenv('CAPTCHA_SITEKEY') ?: ""; + self::$CAPTCHA_ENDPOINT = getenv('CAPTCHA_ENDPOINT') ?: "https://eu-api.friendlycaptcha.eu/api/v1/siteverify"; + self::$CAPTCHA_ENDPOINT_PUZZLE = getenv('CAPTCHA_ENDPOINT_PUZZLE') ?: "https://eu-api.friendlycaptcha.eu/api/v1/puzzle"; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 8f39b7b78..026b15969 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -5,7 +5,7 @@ use BO\Zmscitizenapi\Application; use BO\Zmscitizenapi\BaseController; use BO\Zmscitizenapi\Helper\UtilityHelper; -use BO\Zmscitizenapi\Services\CaptchaService; +use BO\Zmscitizenapi\Services\FriendlyCaptchaService; use BO\Zmscitizenapi\Services\MapperService; use BO\Zmscitizenapi\Services\ValidationService; use BO\Zmscitizenapi\Services\ZmsApiFacadeService; @@ -41,7 +41,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $captchaRequired = Application::$CAPTCHA_ENABLED === true && $providerScope['captchaActivatedRequired'] === "1"; if ($captchaRequired) { - $captchaVerificationResult = CaptchaService::verifyCaptcha($captchaSolution); + $captchaVerificationResult = FriendlyCaptchaService::verifyCaptcha($captchaSolution); if (!$captchaVerificationResult['success']) { return $this->createJsonResponse($response, [ 'errorCode' => 'captchaVerificationFailed', diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index 4dcbed6b9..f101cc9dd 100644 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -5,13 +5,13 @@ use BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\CaptchaService; +use BO\Zmscitizenapi\Services\FriendlyCaptchaService; class CaptchaGet extends BaseController { public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - $captchaDetails = CaptchaService::getCaptchaDetails(); + $captchaDetails = FriendlyCaptchaService::getCaptchaDetails(); return $this->createJsonResponse($response, $captchaDetails, statusCode: $captchaDetails['status']); } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php new file mode 100644 index 000000000..ba2e0a13b --- /dev/null +++ b/zmscitizenapi/src/Zmscitizenapi/Services/CaptchaServiceInterface.php @@ -0,0 +1,22 @@ + Application::$FRIENDLYCAPTCHA_SITEKEY, - 'captchaEndpoint' => Application::$FRIENDLYCAPTCHA_ENDPOINT, - 'puzzle' => Application::$FRIENDLYCAPTCHA_ENDPOINT_PUZZLE, + 'siteKey' => Application::$CAPTCHA_SITEKEY, + 'captchaEndpoint' => Application::$CAPTCHA_ENDPOINT, + 'puzzle' => Application::$CAPTCHA_ENDPOINT_PUZZLE, 'captchaEnabled' => Application::$CAPTCHA_ENABLED, 'status' => 200 ]; } - public static function verifyCaptcha($solution) + public static function verifyCaptcha(string $solution) { try { - $response = \App::$http->post(Application::$FRIENDLYCAPTCHA_ENDPOINT, [ + $response = \App::$http->post(Application::$CAPTCHA_ENDPOINT, [ 'form_params' => [ - 'secret' => Application::$FRIENDLYCAPTCHA_SECRET, + 'secret' => Application::$CAPTCHA_SECRET, 'solution' => $solution ] ]); @@ -35,8 +34,7 @@ public static function verifyCaptcha($solution) return $responseBody; } catch (RequestException $e) { $errorMessage = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage(); - throw new Exception('Captcha verification failed.'); + throw new Exception('Captcha verification failed: ' . $errorMessage); } } } - diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index 3d6e05f48..1a0c8060c 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -12,9 +12,9 @@ public function setUp(): void { parent::setUp(); - putenv('FRIENDLYCAPTCHA_SITEKEY=FAKE_SITE_KEY'); - putenv('FRIENDLYCAPTCHA_ENDPOINT=https://api.friendlycaptcha.com/api/v1/siteverify'); - putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE=https://api.friendlycaptcha.com/api/v1/puzzle'); + putenv('CAPTCHA_SITEKEY=FAKE_SITE_KEY'); + putenv('CAPTCHA_ENDPOINT=https://api.friendlycaptcha.com/api/v1/siteverify'); + putenv('CAPTCHA_ENDPOINT_PUZZLE=https://api.friendlycaptcha.com/api/v1/puzzle'); putenv('CAPTCHA_ENABLED=1'); Application::initialize(); @@ -22,9 +22,9 @@ public function setUp(): void public function tearDown(): void { - putenv('FRIENDLYCAPTCHA_SITEKEY='); - putenv('FRIENDLYCAPTCHA_ENDPOINT='); - putenv('FRIENDLYCAPTCHA_ENDPOINT_PUZZLE='); + putenv('CAPTCHA_SITEKEY='); + putenv('CAPTCHA_ENDPOINT='); + putenv('CAPTCHA_ENDPOINT_PUZZLE='); putenv('CAPTCHA_ENABLED='); parent::tearDown(); From f4085ffae974bce012b0eae5ce5664c286c3abc0 Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Tue, 26 Nov 2024 17:13:37 +0100 Subject: [PATCH 28/42] clean(MPDZBS-877): improve validation for array ids --- .../Zmscitizenapi/OfficesByServiceList.php | 19 ++++++++-- .../src/Zmscitizenapi/ScopeByIdGet.php | 19 +++++++--- .../Services/ValidationService.php | 35 +++++++++++++++++++ .../OfficesByServiceListTest.php | 20 +++++++++++ .../tests/Zmscitizenapi/ScopeByIdGetTest.php | 19 ++++++++++ 5 files changed, 104 insertions(+), 8 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php index dc3c2e475..6df17b579 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -6,21 +6,34 @@ use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use BO\Zmscitizenapi\Services\ValidationService; class OfficesByServiceList extends BaseController { public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - $serviceIds = explode(',', $request->getQueryParams()['serviceId'] ?? ''); - $result = ZmsApiFacadeService::getOfficesByServiceIds($serviceIds); + $serviceIdParam = $request->getQueryParams()['serviceId'] ?? []; + if (is_string($serviceIdParam)) { + $serviceIdParam = explode(',', $serviceIdParam); + } + + $errors = ValidationService::validateServiceIdParam($serviceIdParam); + if (!empty($errors)) { + return $this->createJsonResponse($response, ['errors' => $errors], 400); + } + + $result = ZmsApiFacadeService::getOfficesByServiceIds($serviceIdParam); + if (isset($result['errors'])) { return $this->createJsonResponse($response, $result, $result['status']); } - + return $this->createJsonResponse($response, $result['offices'], $result['status']); } + + } diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php index ba57dd411..7a180bb64 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -4,6 +4,7 @@ use BO\Zmscitizenapi\BaseController; use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use BO\Zmscitizenapi\Services\ValidationService; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -11,15 +12,23 @@ class ScopeByIdGet extends BaseController { public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { - $scopeIds = explode(',', $request->getQueryParams()['scopeId'] ?? ''); - - $result = ZmsApiFacadeService::getScopeByIds($scopeIds); - + $scopeIdParam = $request->getQueryParams()['scopeId'] ?? []; + + if (is_string($scopeIdParam)) { + $scopeIdParam = explode(',', $scopeIdParam); + } + + $errors = ValidationService::validateScopeIdParam($scopeIdParam); + if (!empty($errors)) { + return $this->createJsonResponse($response, ['errors' => $errors], 400); + } + + $result = ZmsApiFacadeService::getScopeByIds($scopeIdParam); + if (isset($result['errors'])) { return $this->createJsonResponse($response, $result, $result['status']); } return $this->createJsonResponse($response, $result['scopes'], $result['status']); } - } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 21da3485a..5b119146d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -2,6 +2,8 @@ namespace BO\Zmscitizenapi\Services; +use \BO\Mellon\Validator; + class ValidationService { @@ -364,7 +366,40 @@ public static function validateUpdateAppointmentInputs($processId, $authKey, $fa return ['status' => 200, 'message' => 'Valid input for updating appointment.']; } + + public static function validateServiceIdParam(array $serviceIds): array + { + $errors = []; + + foreach ($serviceIds as $id) { + if (!is_numeric($id)) { + $errors[] = [ + 'offices' => [], + 'errorMessage' => "Invalid service ID: $id. Must be a number.", + 'status' => 400, + ]; + } + } + + return empty($errors) ? [] : $errors; + } + public static function validateScopeIdParam(array $serviceIds): array + { + $errors = []; + + foreach ($serviceIds as $id) { + if (!is_numeric($id)) { + $errors[] = [ + 'scopes' => [], + 'errorMessage' => "Invalid scope ID: $id. Must be a number.", + 'status' => 400, + ]; + } + } + + return empty($errors) ? [] : $errors; + } } diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php index c61c1c5a9..aab51e2eb 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php @@ -267,4 +267,24 @@ public function testDuplicateServiceIdsCombinable() $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } + + public function testInvalidServiceId() + { + $response = $this->render([], [ + 'serviceId' => 'blahblahblah' + ], []); + + $expectedResponse = [ + 'errors' => [ + [ + 'offices' => [], + 'errorMessage' => 'Invalid service ID: blahblahblah. Must be a number.', + 'status' => 400, + ] + ] + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + } diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php index 4a3b95ca9..e6b2a91dc 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -231,5 +231,24 @@ public function testDuplicateScopeIds() $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); } + + public function testInvalidScopeId() + { + $response = $this->render([], [ + 'scopeId' => 'blahblahblah' + ], []); + $expectedResponse = [ + 'errors' => [ + [ + 'scopes' => [], + 'errorMessage' => 'Invalid scope ID: blahblahblah. Must be a number.', + 'status' => 400 + ] + ] + ]; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + } + } From 5c27510392b278326567127d53c9790b2462673f Mon Sep 17 00:00:00 2001 From: ThomasAFink Date: Thu, 28 Nov 2024 20:06:34 +0100 Subject: [PATCH 29/42] feat(MPDZBS-877): improve zmscitizenapi typing --- zmscitizenapi/routing.php | 2 +- .../src/Zmscitizenapi/AppointmentGet.php | 4 +- .../src/Zmscitizenapi/AppointmentReserve.php | 76 +++--- .../Zmscitizenapi/Services/MapperService.php | 11 +- .../Services/ValidationService.php | 58 ++--- .../Services/ZmsApiFacadeService.php | 244 +++++++++--------- .../Zmscitizenapi/AppointmentReserveTest.php | 59 ++--- .../AvailableAppointmentsListTest.php | 16 +- .../tests/Zmscitizenapi/ScopeByIdGetTest.php | 176 ++++++------- .../fixtures/GET_appointments.json | 2 +- .../fixtures/GET_appointments_free.json | 2 +- .../Zmscitizenapi/fixtures/GET_calendar.json | 2 +- .../fixtures/GET_calendar_empty_days.json | 2 +- .../fixtures/GET_reserve_SourceGet_dldb.json | 6 +- 14 files changed, 331 insertions(+), 329 deletions(-) diff --git a/zmscitizenapi/routing.php b/zmscitizenapi/routing.php index 8f7a4699e..0c8d5fe44 100644 --- a/zmscitizenapi/routing.php +++ b/zmscitizenapi/routing.php @@ -108,7 +108,7 @@ * tags: * - scopes * parameters: - * - name: id + * - name: scopeId * description: Scope ID * in: query * required: true diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php index 556f4d0c6..8ea76ffea 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php @@ -12,8 +12,8 @@ class AppointmentGet extends BaseController public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $queryParams = $request->getQueryParams(); - $processId = $queryParams['processId'] ?? null; - $authKey = $queryParams['authKey'] ?? null; + $processId = isset($queryParams['processId']) && is_numeric($queryParams['processId']) ? (int)$queryParams['processId'] : null; + $authKey = isset($queryParams['authKey']) && is_string($queryParams['authKey']) && trim($queryParams['authKey']) !== '' ? $queryParams['authKey'] : null; $result = ZmsApiFacadeService::getProcessById($processId, $authKey); diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 026b15969..16007f3ea 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -9,6 +9,9 @@ use BO\Zmscitizenapi\Services\MapperService; use BO\Zmscitizenapi\Services\ValidationService; use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmsentities\Process; +use \BO\Zmsentities\Scope; +use \BO\Zmsentities\Collection\ScopeList; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -16,30 +19,27 @@ class AppointmentReserve extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) { $request = $request instanceof ServerRequestInterface ? $request : null; - + $body = $request->getParsedBody(); - - $officeId = $body['officeId'] ?? null; - $serviceIds = $body['serviceId'] ?? []; + + $officeId = isset($body['officeId']) && is_numeric($body['officeId']) ? (int) $body['officeId'] : null; + $serviceIds = $body['serviceId'] ?? null; $serviceCounts = $body['serviceCount'] ?? [1]; $captchaSolution = $body['captchaSolution'] ?? null; - $timestamp = $body['timestamp'] ?? null; - - - $errors = ValidationService::validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $captchaSolution, $timestamp); + $timestamp = isset($body['timestamp']) && is_numeric($body['timestamp']) ? (int) $body['timestamp'] : null; + + $errors = ValidationService::validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $timestamp); if (!empty($errors['errors'])) { - return $this->createJsonResponse($response, - $errors, 400); + return $this->createJsonResponse($response, $errors, 400); } - + try { $providerScope = ZmsApiFacadeService::getScopeByOfficeId($officeId); $captchaRequired = Application::$CAPTCHA_ENABLED === true && $providerScope['captchaActivatedRequired'] === "1"; - + if ($captchaRequired) { $captchaVerificationResult = FriendlyCaptchaService::verifyCaptcha($captchaSolution); if (!$captchaVerificationResult['success']) { @@ -49,58 +49,72 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo ], 400); } } - + $serviceValidationResult = ValidationService::validateServiceLocationCombination($officeId, $serviceIds); if ($serviceValidationResult['status'] !== 200) { return $this->createJsonResponse($response, $serviceValidationResult, 400); } - + $freeAppointments = ZmsApiFacadeService::getFreeAppointments([ 'officeId' => $officeId, 'serviceIds' => $serviceIds, 'serviceCounts' => $serviceCounts, 'date' => UtilityHelper::getInternalDateFromTimestamp($timestamp) ]); - - $selectedProcess = array_filter($freeAppointments, function ($process) use ($timestamp) { + + $filteredProcesses = array_filter($freeAppointments, function ($process) use ($timestamp) { if (!isset($process['appointments']) || !is_array($process['appointments'])) { return false; } return in_array($timestamp, array_column($process['appointments'], 'date')); }); - + + $selectedProcess = null; + + if (!empty($filteredProcesses)) { + $selectedProcessData = array_values($filteredProcesses)[0]; + + $selectedProcess = new Process(); + $scopeData = $selectedProcessData['scope'] ?? null; + $scope = $scopeData ? new Scope($scopeData) : null; + + $selectedProcess->withUpdatedData($selectedProcessData, new \DateTime("@$timestamp"), $scope); + } + $errors = ValidationService::validateGetProcessNotFound($selectedProcess); if (!empty($errors['errors'])) { - return $this->createJsonResponse($response, - $errors, 404); + return $this->createJsonResponse($response, $errors, 404); } - - $selectedProcess = array_values($selectedProcess)[0]; - $selectedProcess['clients'] = [ + + $selectedProcess->clients = [ [ 'email' => 'default@example.com' ] ]; - + $reservedProcess = ZmsApiFacadeService::reserveTimeslot($selectedProcess, $serviceIds, $serviceCounts); - + if ($reservedProcess && $reservedProcess->scope && $reservedProcess->scope->id) { $scopeIds = [$reservedProcess->scope->id]; $scopesData = ZmsApiFacadeService::getScopeByIds($scopeIds); - + if ($scopesData['status'] === 200 && isset($scopesData['scopes']['scopes']) && !empty($scopesData['scopes']['scopes'])) { $reservedProcess->scope = MapperService::mapScope($scopesData['scopes']['scopes'][0]); } - } - + } + $thinnedProcessData = UtilityHelper::getThinnedProcessData($reservedProcess); $thinnedProcessData = array_merge($thinnedProcessData, ['officeId' => $officeId]); - + return $this->createJsonResponse($response, $thinnedProcessData, 200); - + } catch (\Exception $e) { - throw $e; + return $this->createJsonResponse($response, [ + 'errorCode' => 'internalServerError', + 'errorMessage' => $e->getMessage() + ], 500); } } + } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php index e0738beed..c2d0baa97 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -2,6 +2,8 @@ namespace BO\Zmscitizenapi\Services; +use \BO\Zmsentities\Collection\ScopeList; + class MapperService { @@ -27,14 +29,17 @@ public static function mapScope($scope) public static function mapOfficesWithScope($providerList) { $offices = []; + + $scopes = new ScopeList(ZmsApiClientService::getScopes() ?? []); + foreach ($providerList as $provider) { $officeData = [ "id" => $provider->id, "name" => $provider->displayName ?? $provider->name, ]; - $scope = ZmsApiFacadeService::getScopeForProvider($provider->id); - if ($scope) { - $officeData['scope'] = $scope; + $providerScope = ZmsApiFacadeService::getScopeForProvider($provider->id, $scopes); + if (isset($providerScope['scope'])) { + $officeData['scope'] = $providerScope['scope']; } $offices[] = $officeData; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 5b119146d..916799979 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -4,10 +4,15 @@ use \BO\Mellon\Validator; +use \BO\Zmsentities\Process; +use \BO\Zmsentities\Scope; +use \BO\Zmsentities\Collection\ProcessList; +use \BO\Zmsentities\Collection\ScopeList; + class ValidationService { - public static function validateServiceLocationCombination($officeId, array $serviceIds) + public static function validateServiceLocationCombination(int $officeId, array $serviceIds): array { $availableServices = ZmsApiFacadeService::getServicesProvidedAtOffice($officeId); $availableServiceIds = array_map(function ($service) { @@ -34,7 +39,7 @@ public static function validateServiceLocationCombination($officeId, array $serv ]; } - public static function validateGetBookableFreeDays($officeId, $serviceId, $startDate, $endDate, $serviceCounts) + public static function validateGetBookableFreeDays(?int $officeId, ?int $serviceId, ?string $startDate, ?string $endDate, ?array $serviceCounts): array { $errors = []; if (!$startDate) { @@ -56,7 +61,7 @@ public static function validateGetBookableFreeDays($officeId, $serviceId, $start return ['errors' => $errors, 'status' => 400]; } - public static function validateGetProcessById($processId, $authKey) + public static function validateGetProcessById(?int $processId, ?string $authKey): array { $errors = []; if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { @@ -76,7 +81,7 @@ public static function validateGetProcessById($processId, $authKey) return ['errors' => $errors, 'status' => 400]; } - public static function validateGetAvailableAppointments($date, $officeId, $serviceIds, $serviceCounts) + public static function validateGetAvailableAppointments(?string $date, ?int $officeId, ?array $serviceIds, ?array $serviceCounts): array { $errors = []; if (!$date) { @@ -109,18 +114,14 @@ public static function validateGetAvailableAppointments($date, $officeId, $servi return ['errors' => $errors, 'status' => 400]; } - public static function validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $captchaSolution, $timestamp) + + public static function validatePostAppointmentReserve(?int $officeId, ?array $serviceIds, ?array $serviceCounts, ?int $timestamp): array { $errors = []; - if (!$officeId) { - $errors[] = [ - 'status' => 400, - 'errorMessage' => 'Missing officeId.', - ]; - } elseif (!is_numeric($officeId)) { + if (!$officeId || !is_numeric($officeId)) { $errors[] = [ 'status' => 400, - 'errorMessage' => 'Invalid officeId format. It should be a numeric value.', + 'errorMessage' => 'officeId should be a 32-bit integer.', ]; } @@ -136,15 +137,10 @@ public static function validatePostAppointmentReserve($officeId, $serviceIds, $s ]; } - if (!$timestamp) { - $errors[] = [ - 'status' => 400, - 'errorMessage' => 'Missing timestamp.', - ]; - } elseif (!is_numeric($timestamp) || $timestamp < 0) { + if (!$timestamp || !is_numeric($timestamp) || $timestamp < 0) { $errors[] = [ 'status' => 400, - 'errorMessage' => 'Invalid timestamp format. It should be a positive numeric value.', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ]; } @@ -158,7 +154,7 @@ public static function validatePostAppointmentReserve($officeId, $serviceIds, $s return ['errors' => $errors, 'status' => 400]; } - public static function validateGetOfficesByServiceIds($serviceIds) + public static function validateGetOfficesByServiceIds(?array $serviceIds): array { $errors = []; if (empty($serviceIds) || $serviceIds == ['']) { @@ -172,7 +168,7 @@ public static function validateGetOfficesByServiceIds($serviceIds) return ['errors' => $errors, 'status' => 400]; } - public static function validateGetScopeByIds($scopeIds) + public static function validateGetScopeByIds(?array $scopeIds): array { $errors = []; if (empty($scopeIds) || $scopeIds == ['']) { @@ -186,7 +182,7 @@ public static function validateGetScopeByIds($scopeIds) return ['errors' => $errors, 'status' => 400]; } - public static function validateGetServicesByOfficeIds($officeIds) + public static function validateGetServicesByOfficeIds(?array $officeIds): array { $errors = []; @@ -201,7 +197,7 @@ public static function validateGetServicesByOfficeIds($officeIds) return ['errors' => $errors, 'status' => 400]; } - public static function validateGetProcessFreeSlots($freeSlots) + public static function validateGetProcessFreeSlots(?ProcessList $freeSlots): array { $errors = []; if (empty($freeSlots) || !is_iterable($freeSlots)) { @@ -216,7 +212,7 @@ public static function validateGetProcessFreeSlots($freeSlots) return ['errors' => $errors, 'status' => 404]; } - public static function validateGetProcessByIdTimestamps($appointmentTimestamps) + public static function validateGetProcessByIdTimestamps(?array $appointmentTimestamps): array { $errors = []; if (empty($appointmentTimestamps)) { @@ -231,7 +227,7 @@ public static function validateGetProcessByIdTimestamps($appointmentTimestamps) return ['errors' => $errors, 'status' => 404]; } - public static function validateGetProcessNotFound($process) + public static function validateGetProcessNotFound(?Process $process): array { $errors = []; if (!$process) { @@ -245,10 +241,10 @@ public static function validateGetProcessNotFound($process) return ['errors' => $errors, 'status' => 404]; } - public static function validateScopesNotFound($scopes) + public static function validateScopesNotFound(?ScopeList $scopes): array { $errors = []; - if (empty($scopes)) { + if (empty($scopes) || $scopes === null || $scopes->count() === 0) { $errors[] = [ 'errorCode' => 'scopesNotFound', 'errorMessage' => 'Scope(s) not found.', @@ -259,7 +255,7 @@ public static function validateScopesNotFound($scopes) return ['errors' => $errors, 'status' => 404]; } - public static function validateServicesNotFound($services) + public static function validateServicesNotFound(?array $services): array { $errors = []; if (empty($services)) { @@ -273,7 +269,7 @@ public static function validateServicesNotFound($services) return ['errors' => $errors, 'status' => 404]; } - public static function validateOfficesNotFound($offices) + public static function validateOfficesNotFound(?array $offices): array { $errors = []; if (empty($offices)) { @@ -287,7 +283,7 @@ public static function validateOfficesNotFound($offices) return ['errors' => $errors, 'status' => 404]; } - public static function validateAppointmentDaysNotFound($formattedDays) + public static function validateAppointmentDaysNotFound(?array $formattedDays): array { $errors = []; if (empty($formattedDays)) { @@ -301,7 +297,7 @@ public static function validateAppointmentDaysNotFound($formattedDays) return ['errors' => $errors, 'status' => 404]; } - public static function validateNoAppointmentsAtLocation() + public static function validateNoAppointmentsAtLocation(): array { $errors[] = [ diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index 0aef3d2e2..e2d034270 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -3,30 +3,27 @@ namespace BO\Zmscitizenapi\Services; use BO\Zmscitizenapi\Helper\UtilityHelper; +use \BO\Zmsentities\Process; +use \BO\Zmsentities\Scope; +use \BO\Zmsentities\Collection\ScopeList; class ZmsApiFacadeService { - public static function getOffices() + public static function getOffices(): array { - $scopeList = ZmsApiClientService::getScopes() ?? []; + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); $providerProjectionList = []; - + foreach (ZmsApiClientService::getOffices() as $provider) { - $matchingScope = null; - foreach ($scopeList as $scope) { - if ($scope->provider->id == $provider->id) { - $matchingScope = $scope; - break; - } - } - + $matchingScope = $scopeList->withProviderID($provider->source, $provider->id)->getIterator()->current(); + $providerData = [ "id" => $provider->id, "name" => $provider->displayName ?? $provider->name, ]; - - if ($matchingScope) { + + if ($matchingScope instanceof Scope) { $providerData["scope"] = [ "id" => $matchingScope->id, "provider" => $matchingScope->provider, @@ -40,26 +37,25 @@ public static function getOffices() "displayInfo" => $matchingScope->getDisplayInfo() ]; } - + $providerProjectionList[] = $providerData; } - + return [ "offices" => $providerProjectionList, "status" => 200 ]; } - - - public static function getScopes() + + public static function getScopes(): array { - $scopeList = ZmsApiClientService::getScopes() ?? []; + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); $scopesProjectionList = []; - + foreach ($scopeList as $scope) { $scopesProjectionList[] = [ "id" => $scope->id, - "provider" => $scope->provider, + "provider" => $scope->getProvider(), "shortName" => $scope->shortName, "telephoneActivated" => $scope->getTelephoneActivated(), "telephoneRequired" => $scope->getTelephoneRequired(), @@ -70,14 +66,14 @@ public static function getScopes() "displayInfo" => $scope->getDisplayInfo() ]; } - + return [ "scopes" => $scopesProjectionList, "status" => 200 ]; } - - public static function getServices() + + public static function getServices(): array { $requestList = ZmsApiClientService::getServices() ?? []; @@ -98,29 +94,40 @@ public static function getServices() ]; } - public static function getScopeForProvider($providerId) + public static function getScopeForProvider(?int $providerId, ?ScopeList $scopes): array { - $scopeList = ZmsApiClientService::getScopes() ?? []; - foreach ($scopeList as $scope) { - if ($scope->provider->id === $providerId) { - return [ - "id" => $scope->id, - "provider" => $scope->provider, - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), - "displayInfo" => $scope->getDisplayInfo() - ]; + $filteredScopes = $scopes->withProviderID(\App::$source_name, (string)$providerId); + + if ($filteredScopes->count() === 0) { + $errors = ValidationService::validateScopesNotFound($filteredScopes); + if (!empty($errors['errors'])) { + return $errors; } } - return null; + + $result = []; + foreach ($filteredScopes as $scope) { + $result = [ + "id" => $scope->id, + "provider" => $scope->getProvider(), + "shortName" => $scope->shortName, + "telephoneActivated" => $scope->getTelephoneActivated(), + "telephoneRequired" => $scope->getTelephoneRequired(), + "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), + "displayInfo" => $scope->getDisplayInfo(), + ]; + } + + return [ + 'scope' => $result, + 'status' => 200, + ]; } - - public static function getServicesAndOffices() + + public static function getServicesAndOffices(): array { $providerList = ZmsApiClientService::getOffices() ?? []; $requestList = ZmsApiClientService::getServices() ?? []; @@ -145,28 +152,32 @@ public static function getServicesAndOffices() * */ - public static function getScopeByOfficeId($officeId) - { - try { - $scopes = self::getScopes(); - - foreach ($scopes as $scope) { - if (isset($scope['provider']) && $scope['provider']->id == $officeId) { - return $scope; - } - } - - return [ - 'error' => 'Scope not found for the provided office ID', - 'status' => 404 - ]; - } catch (\Exception $e) { - return [ - 'error' => 'Error fetching scope by office ID: ' . $e->getMessage(), - 'status' => 500 - ]; - } - } + public static function getScopeByOfficeId($officeId): array + { + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); + $matchingScope = $scopeList->withProviderID('dldb', $officeId)->getIterator()->current(); + + if ($matchingScope instanceof Scope) { + return [ + "id" => $matchingScope->id, + "provider" => $matchingScope->provider, + "shortName" => $matchingScope->shortName, + "telephoneActivated" => $matchingScope->getTelephoneActivated(), + "telephoneRequired" => $matchingScope->getTelephoneRequired(), + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), + "displayInfo" => $matchingScope->getDisplayInfo(), + ]; + } + + return [ + 'error' => 'Scope not found for the provided office ID', + 'status' => 404, + ]; + } + /* Todo add method * getOfficeByIds @@ -175,7 +186,7 @@ public static function getScopeByOfficeId($officeId) * */ - public static function getOfficesByServiceIds(array $serviceIds) + public static function getOfficesByServiceIds(array $serviceIds): array { $serviceIds = array_unique($serviceIds); @@ -233,65 +244,52 @@ public static function getOfficesByServiceIds(array $serviceIds) ]; } - public static function getScopeByIds(array $scopeIds) + public static function getScopeByIds(array $scopeIds): array { $scopeIds = array_unique($scopeIds); - + $errors = ValidationService::validateGetScopeByIds($scopeIds); if (!empty($errors['errors'])) { return $errors; } - - $scopeList = ZmsApiClientService::getScopes() ?? []; - $scopes = []; - $notFoundIds = []; - - foreach ($scopeIds as $scopeId) { - $found = false; - foreach ($scopeList as $scopeItem) { - if ($scopeItem->id == $scopeId) { - $scopes[] = [ - "id" => $scopeItem->id, - "provider" => [ - "id" => $scopeItem->provider->id, - "source" => $scopeItem->provider->source, - ], - "shortName" => $scopeItem->shortName, - "telephoneActivated" => $scopeItem->getTelephoneActivated(), - "telephoneRequired" => $scopeItem->getTelephoneRequired(), - "customTextfieldActivated" => $scopeItem->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scopeItem->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scopeItem->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scopeItem->getCaptchaActivatedRequired(), - "displayInfo" => $scopeItem->getDisplayInfo() - ]; - $found = true; - break; - } - } - - if (!$found) { - $notFoundIds[] = $scopeId; + + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); + $filteredScopes = new ScopeList(); + + foreach ($scopeList as $scope) { + if (in_array($scope->id, $scopeIds)) { + $filteredScopes->addEntity($scope); } } - - $errors = ValidationService::validateScopesNotFound($scopes); + + $errors = ValidationService::validateScopesNotFound($filteredScopes); if (!empty($errors['errors'])) { return $errors; } - - $responseContent = ['scopes' => $scopes]; - if (!empty($notFoundIds)) { - $responseContent['warning'] = 'The following scopeId(s) were not found: ' . implode(', ', $notFoundIds); + + $scopes = []; + foreach ($filteredScopes as $scope) { + $scopes[] = [ + "id" => $scope->id, + "provider" => $scope->getProvider(), + "shortName" => $scope->shortName, + "telephoneActivated" => $scope->getTelephoneActivated(), + "telephoneRequired" => $scope->getTelephoneRequired(), + "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), + "displayInfo" => $scope->getDisplayInfo(), + ]; } - + return [ - 'scopes' => $responseContent, - 'status' => 200 + 'scopes' => $scopes, + 'status' => 200, ]; } - public static function getServicesByOfficeIds(array $officeIds) + public static function getServicesByOfficeIds(array $officeIds): array { $officeIds = array_unique($officeIds); @@ -352,7 +350,7 @@ public static function getServicesByOfficeIds(array $officeIds) * */ - public static function getServicesProvidedAtOffice($officeId) + public static function getServicesProvidedAtOffice(int $officeId): array { $requestRelationList = ZmsApiClientService::getRequestRelationList(); @@ -381,7 +379,7 @@ public static function getServicesProvidedAtOffice($officeId) }); } - public static function getBookableFreeDays(array $queryParams) + public static function getBookableFreeDays(array $queryParams): array { $officeId = $queryParams['officeId'] ?? null; $serviceId = $queryParams['serviceId'] ?? null; @@ -432,7 +430,7 @@ public static function getBookableFreeDays(array $queryParams) return ExceptionService::noAppointmentsAtLocation(); } } - public static function getFreeAppointments(array $params) + public static function getFreeAppointments(array $params): array { $office = [ 'id' => $params['officeId'], @@ -475,7 +473,7 @@ public static function getFreeAppointments(array $params) } } - public static function getAvailableAppointments(array $queryParams) + public static function getAvailableAppointments(array $queryParams): array { $date = $queryParams['date'] ?? null; $officeId = $queryParams['officeId'] ?? null; @@ -523,7 +521,7 @@ public static function getAvailableAppointments(array $queryParams) } } - private static function processFreeSlots($freeSlots) + private static function processFreeSlots($freeSlots): array { $errors = ValidationService::validateGetProcessFreeSlots($freeSlots); @@ -564,12 +562,12 @@ private static function processFreeSlots($freeSlots) ]; } - public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) + public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts): Process { return ZmsApiClientService::reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts); } - public static function getProcessById($processId, $authKey) + public static function getProcessById(?int $processId, ?string $authKey): array { $errors = ValidationService::validateGetProcessById($processId, $authKey); if (!empty($errors['errors'])) { @@ -577,9 +575,9 @@ public static function getProcessById($processId, $authKey) } try { + $process = new Process; $process = ZmsApiClientService::getProcessById($processId, $authKey); - $errors = ValidationService::validateGetProcessNotFound($process); if (!empty($errors['errors'])) { return $errors; @@ -597,18 +595,16 @@ public static function getProcessById($processId, $authKey) } } - public static function updateClientData($reservedProcess) + public static function updateClientData($reservedProcess): Process|array { - $clientUpdateResult = ZmsApiClientService::submitClientData($reservedProcess); - - if (!isset($clientUpdateResult['error'])) { + + if (isset($clientUpdateResult['error'])) { return $clientUpdateResult; } - + return $clientUpdateResult; - } - + } /* Todo add method * preconfirmAppointment diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php index 6f7959a5d..e57e4bebf 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -32,15 +32,15 @@ public function testRendering() ] ] ); - + $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => ['1063423'], 'serviceCount' => [0], 'timestamp' => "32526616522", 'captchaSolution' => null ]; - + $response = $this->render([], $parameters, [], 'POST'); $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ @@ -52,26 +52,17 @@ public function testRendering() 'email' => 'default@example.com', 'telephone' => '123456789', 'officeName' => null, - 'officeId' => '10546', + 'officeId' => 10546, 'scope' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/scope.json', 'id' => '58', - 'provider' => [ - 'id' => '10546', - 'source' => 'dldb' - ], - 'shortName' => 'Gewerbemeldungen', - 'telephoneActivated' => '0', - 'telephoneRequired' => '1', - 'customTextfieldActivated' => '0', - 'customTextfieldRequired' => '1', - 'customTextfieldLabel' => '', - 'captchaActivatedRequired' => '0', - 'displayInfo' => null + 'source' => 'dldb', ], 'subRequestCounts' => [], 'serviceId' => null, 'serviceCount' => 0 ]; + $this->assertEquals(200, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } @@ -97,7 +88,7 @@ public function testAppointmentNotAvailable() ); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => ['1063423'], 'serviceCount' => [0], 'timestamp' => "32526616300", @@ -137,7 +128,7 @@ public function testMissingOfficeId() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing officeId.', + 'errorMessage' => 'officeId should be a 32-bit integer.', ] ], 'status' => 400 @@ -151,7 +142,7 @@ public function testMissingServiceId() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceCount' => [0], 'timestamp' => "32526616522", 'captchaSolution' => null @@ -177,7 +168,7 @@ public function testMissingTimestamp() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => ['1063423'], 'serviceCount' => [0], 'captchaSolution' => null @@ -189,7 +180,7 @@ public function testMissingTimestamp() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing timestamp.', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], 'status' => 400 @@ -214,7 +205,7 @@ public function testMissingOfficeIdAndServiceId() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing officeId.', + 'errorMessage' => 'officeId should be a 32-bit integer.', ], [ 'status' => 400, @@ -243,11 +234,11 @@ public function testMissingOfficeIdAndTimestamp() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing officeId.', + 'errorMessage' => 'officeId should be a 32-bit integer.', ], [ 'status' => 400, - 'errorMessage' => 'Missing timestamp.', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], 'status' => 400 @@ -261,7 +252,7 @@ public function testMissingServiceIdAndTimestamp() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceCount' => [0], 'captchaSolution' => null ]; @@ -276,7 +267,7 @@ public function testMissingServiceIdAndTimestamp() ], [ 'status' => 400, - 'errorMessage' => 'Missing timestamp.', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], 'status' => 400 @@ -297,7 +288,7 @@ public function testMissingAllFields() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing officeId.', + 'errorMessage' => 'officeId should be a 32-bit integer.', ], [ 'status' => 400, @@ -305,7 +296,7 @@ public function testMissingAllFields() ], [ 'status' => 400, - 'errorMessage' => 'Missing timestamp.', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], 'status' => 400 @@ -332,7 +323,7 @@ public function testInvalidOfficeIdFormat() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Invalid officeId format. It should be a numeric value.', + 'errorMessage' => 'officeId should be a 32-bit integer.', ] ], 'status' => 400 @@ -346,7 +337,7 @@ public function testInvalidServiceIdFormat() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => ['invalid_service_id'], 'serviceCount' => [0], 'timestamp' => "32526616522", @@ -373,7 +364,7 @@ public function testInvalidTimestampFormat() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => ['1063423'], 'serviceCount' => [0], 'timestamp' => 'invalid_timestamp', @@ -386,7 +377,7 @@ public function testInvalidTimestampFormat() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Invalid timestamp format. It should be a positive numeric value.', + 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], 'status' => 400 @@ -399,7 +390,7 @@ public function testEmptyServiceIdArray() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => [], 'serviceCount' => [0], 'timestamp' => "32526616522", @@ -426,7 +417,7 @@ public function testInvalidServiceCount() $this->setApiCalls([]); $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => ['1063423'], 'serviceCount' => ['invalid'], 'timestamp' => "32526616522", diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php index 37e895878..de632ec2e 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php @@ -20,7 +20,7 @@ public function testRendering() $parameters = [ 'date' => '3000-09-21', - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => '1063423', 'serviceCount' => '1', ]; @@ -50,7 +50,7 @@ public function testEmptyAppointments() $parameters = [ 'date' => '3000-09-21', - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => '1063423', 'serviceCount' => '1', ]; @@ -74,7 +74,7 @@ public function testEmptyAppointments() public function testDateMissing() { $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => '1063423', 'serviceCount' => '1', ]; @@ -121,7 +121,7 @@ public function testServiceIdMissing() { $parameters = [ 'date' => '3000-09-21', - 'officeId' => '10546', + 'officeId' => 10546, 'serviceCount' => '1', ]; @@ -144,7 +144,7 @@ public function testServiceCountMissing() { $parameters = [ 'date' => '3000-09-21', - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => '1063423', ]; @@ -192,7 +192,7 @@ public function testDateAndOfficeIdMissing() public function testDateAndServiceIdMissing() { $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceCount' => '1', ]; @@ -218,7 +218,7 @@ public function testDateAndServiceIdMissing() public function testDateAndServiceCountMissing() { $parameters = [ - 'officeId' => '10546', + 'officeId' => 10546, 'serviceId' => '1063423', ]; @@ -297,7 +297,7 @@ public function testServiceIdAndServiceCountMissing() { $parameters = [ 'date' => '3000-09-21', - 'officeId' => '10546', + 'officeId' => 10546, ]; $response = $this->render([], $parameters, []); diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php index e6b2a91dc..43ed4bd6b 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -25,26 +25,26 @@ public function testRendering() 'scopeId' => '1' ], []); $expectedResponse = [ - 'scopes' => [ - [ - 'id' => '1', - 'provider' => [ - 'id' => '9999998', - 'source' => 'unittest', - ], - 'shortName' => 'Scope 1', - 'telephoneActivated' => '1', - 'telephoneRequired' => '0', - 'customTextfieldActivated' => '1', - 'customTextfieldRequired' => '0', - 'customTextfieldLabel' => 'Custom Label', - 'captchaActivatedRequired' => '1', - 'displayInfo' => null - ] + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null ] ]; + $responseBody = json_decode((string) $response->getBody(), true); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testRenderingMulti() @@ -63,41 +63,42 @@ public function testRenderingMulti() 'scopeId' => '1,2' ], []); $expectedResponse = [ - 'scopes' => [ - [ - 'id' => '1', - 'provider' => [ - 'id' => '9999998', - 'source' => 'unittest', - ], - 'shortName' => 'Scope 1', - 'telephoneActivated' => '1', - 'telephoneRequired' => '0', - 'customTextfieldActivated' => '1', - 'customTextfieldRequired' => '0', - 'customTextfieldLabel' => 'Custom Label', - 'captchaActivatedRequired' => '1', - 'displayInfo' => null + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', ], - [ - 'id' => '2', - 'provider' => [ - 'id' => '9999999', - 'source' => 'unittest', - ], - 'shortName' => 'Scope 2', - 'telephoneActivated' => '0', - 'telephoneRequired' => '1', - 'customTextfieldActivated' => '0', - 'customTextfieldRequired' => '1', - 'customTextfieldLabel' => '', - 'captchaActivatedRequired' => '0', - 'displayInfo' => null + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null + ], + [ + 'id' => '2', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999999', + 'source' => 'unittest', ], + 'shortName' => 'Scope 2', + 'telephoneActivated' => '0', + 'telephoneRequired' => '1', + 'customTextfieldActivated' => '0', + 'customTextfieldRequired' => '1', + 'customTextfieldLabel' => '', + 'captchaActivatedRequired' => '0', + 'displayInfo' => null ] ]; + $responseBody = json_decode((string) $response->getBody(), true); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } public function testScopeNotFound() @@ -151,7 +152,7 @@ public function testNoScopeIdProvided() } - public function testPartialResultsWithWarning() + public function testPartialResultsWithWarning() //Don't return invalid scopes thta don't exist like 99 { $this->setApiCalls([ [ @@ -165,32 +166,32 @@ public function testPartialResultsWithWarning() ]); $response = $this->render([], [ 'scopeId' => '1,99' - ], []); + ], []); + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ - 'scopes' => [ - [ - 'id' => '1', - 'provider' => [ - 'id' => '9999998', - 'source' => 'unittest', - ], - 'shortName' => 'Scope 1', - 'telephoneActivated' => '1', - 'telephoneRequired' => '0', - 'customTextfieldActivated' => '1', - 'customTextfieldRequired' => '0', - 'customTextfieldLabel' => 'Custom Label', - 'captchaActivatedRequired' => '1', - 'displayInfo' => null - ] - ], - 'warning' => 'The following scopeId(s) were not found: 99' + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null + ] ]; + $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); - + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } + public function testDuplicateScopeIds() { $this->setApiCalls([ @@ -207,30 +208,29 @@ public function testDuplicateScopeIds() $response = $this->render([], [ 'scopeId' => '1,1,1' ], []); - + $responseBody = json_decode((string) $response->getBody(), true); $expectedResponse = [ - 'scopes' => [ - [ - 'id' => '1', - 'provider' => [ - 'id' => '9999998', - 'source' => 'unittest', - ], - 'shortName' => 'Scope 1', - 'telephoneActivated' => '1', - 'telephoneRequired' => '0', - 'customTextfieldActivated' => '1', - 'customTextfieldRequired' => '0', - 'customTextfieldLabel' => 'Custom Label', - 'captchaActivatedRequired' => '1', - 'displayInfo' => null - ] + [ + 'id' => '1', + 'provider' => [ + '$schema' => 'https://schema.berlin.de/queuemanagement/provider.json', + 'id' => '9999998', + 'source' => 'unittest', + ], + 'shortName' => 'Scope 1', + 'telephoneActivated' => '1', + 'telephoneRequired' => '0', + 'customTextfieldActivated' => '1', + 'customTextfieldRequired' => '0', + 'customTextfieldLabel' => 'Custom Label', + 'captchaActivatedRequired' => '1', + 'displayInfo' => null ] ]; $this->assertEquals(200, $response->getStatusCode()); - $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); - + $this->assertEqualsCanonicalizing($expectedResponse, $responseBody); } + public function testInvalidScopeId() { diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json index 322b9e35d..f2f1c426a 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments.json @@ -85,7 +85,7 @@ "country": "Germany" }, "provider": { - "id": "10546", + "id": 10546, "source": "dldb", "contact": { "city": "Muenchen", diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json index 1004d3fcb..5f60c4c06 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_appointments_free.json @@ -85,7 +85,7 @@ "country": "Germany" }, "provider": { - "id": "10546", + "id": 10546, "source": "dldb", "contact": { "city": "Muenchen", diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar.json index 7e8346855..5fe3ed970 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar.json @@ -472,7 +472,7 @@ "country": "Germany" }, "provider": { - "id": "10546", + "id": 10546, "source": "dldb", "contact": { "city": "Muenchen", diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar_empty_days.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar_empty_days.json index 67baeeece..a3e62a347 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar_empty_days.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_calendar_empty_days.json @@ -57,7 +57,7 @@ "country": "Germany" }, "provider": { - "id": "10546", + "id": 10546, "source": "dldb", "contact": { "city": "Muenchen", diff --git a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json index 8ae304c7a..ef175b928 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json +++ b/zmscitizenapi/tests/Zmscitizenapi/fixtures/GET_reserve_SourceGet_dldb.json @@ -15,7 +15,7 @@ }, "providers": [ { - "id": "10546", + "id": 10546, "source": "dldb", "contact": { "city": "Muenchen", @@ -51,7 +51,7 @@ "country": "Germany" }, "provider": { - "id": "10546", + "id": 10546, "source": "dldb" }, "hint": "", @@ -128,7 +128,7 @@ "requestrelation": [ { "provider": { - "id": "10546", + "id": 10546, "source": "unittest" }, "request": { From 205e0f0b75c254860a19f0dbaa8fb02349928711 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 12:01:46 +0100 Subject: [PATCH 30/42] clean(MPDZBS-877): typing overhaul using zmsentities --- .../src/Zmscitizenapi/AppointmentCancel.php | 2 +- .../src/Zmscitizenapi/AppointmentConfirm.php | 2 +- .../src/Zmscitizenapi/AppointmentGet.php | 2 +- .../Zmscitizenapi/AppointmentPreconfirm.php | 2 +- .../src/Zmscitizenapi/AppointmentReserve.php | 19 +-- .../src/Zmscitizenapi/AppointmentUpdate.php | 5 +- .../AvailableAppointmentsList.php | 2 +- .../src/Zmscitizenapi/AvailableDaysList.php | 2 +- .../src/Zmscitizenapi/BaseController.php | 4 +- .../src/Zmscitizenapi/CaptchaGet.php | 2 +- .../Zmscitizenapi/Helper/UtilityHelper.php | 9 +- .../Zmscitizenapi/OfficesByServiceList.php | 2 +- .../src/Zmscitizenapi/OfficesList.php | 2 +- .../OfficesServicesRelations.php | 2 +- .../src/Zmscitizenapi/ScopeByIdGet.php | 2 +- .../src/Zmscitizenapi/ScopesList.php | 2 +- .../Services/ExceptionService.php | 9 +- .../Services/FriendlyCaptchaService.php | 2 +- .../Zmscitizenapi/Services/MapperService.php | 12 +- .../Services/ValidationService.php | 7 +- .../Services/ZmsApiClientService.php | 89 +++++------ .../Services/ZmsApiFacadeService.php | 139 +++++++++--------- .../Zmscitizenapi/ServicesByOfficeList.php | 2 +- .../src/Zmscitizenapi/ServicesList.php | 2 +- 24 files changed, 166 insertions(+), 157 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php index bd7e6c06f..9b84031c8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php @@ -9,7 +9,7 @@ class AppointmentCancel extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { return Render::withJson($response, []); } diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php index 931167a1b..4bf172834 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php @@ -9,7 +9,7 @@ class AppointmentConfirm extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { return Render::withJson($response, []); } diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php index 8ea76ffea..05624c63a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php @@ -9,7 +9,7 @@ class AppointmentGet extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $queryParams = $request->getQueryParams(); $processId = isset($queryParams['processId']) && is_numeric($queryParams['processId']) ? (int)$queryParams['processId'] : null; diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php index f1bb45fa8..a7392c2f1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php @@ -9,7 +9,7 @@ class AppointmentPreconfirm extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { return Render::withJson($response, []); } diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 16007f3ea..0d85ceb8b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -11,15 +11,14 @@ use BO\Zmscitizenapi\Services\ZmsApiFacadeService; use \BO\Zmsentities\Process; use \BO\Zmsentities\Scope; -use \BO\Zmsentities\Collection\ScopeList; +use \BO\Zmsentities\Collection\ProcessList; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; class AppointmentReserve extends BaseController { - - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $request = $request instanceof ServerRequestInterface ? $request : null; @@ -55,14 +54,17 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo return $this->createJsonResponse($response, $serviceValidationResult, 400); } + $freeAppointments = new ProcessList(); $freeAppointments = ZmsApiFacadeService::getFreeAppointments([ 'officeId' => $officeId, 'serviceIds' => $serviceIds, 'serviceCounts' => $serviceCounts, 'date' => UtilityHelper::getInternalDateFromTimestamp($timestamp) ]); - - $filteredProcesses = array_filter($freeAppointments, function ($process) use ($timestamp) { + + $processArray = json_decode(json_encode($freeAppointments), true); + + $filteredProcesses = array_filter($processArray, function ($process) use ($timestamp) { if (!isset($process['appointments']) || !is_array($process['appointments'])) { return false; } @@ -92,6 +94,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo ] ]; + $reservedProcess = new Process(); $reservedProcess = ZmsApiFacadeService::reserveTimeslot($selectedProcess, $serviceIds, $serviceCounts); if ($reservedProcess && $reservedProcess->scope && $reservedProcess->scope->id) { @@ -101,7 +104,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo if ($scopesData['status'] === 200 && isset($scopesData['scopes']['scopes']) && !empty($scopesData['scopes']['scopes'])) { $reservedProcess->scope = MapperService::mapScope($scopesData['scopes']['scopes'][0]); } - } + } $thinnedProcessData = UtilityHelper::getThinnedProcessData($reservedProcess); $thinnedProcessData = array_merge($thinnedProcessData, ['officeId' => $officeId]); @@ -115,6 +118,4 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo ], 500); } } - - -} +} \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index baea722c6..cecffabea 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -7,13 +7,14 @@ use BO\Zmscitizenapi\Services\ValidationService; use BO\Zmscitizenapi\Helper\UtilityHelper; use BO\Zmscitizenapi\Services\ExceptionService; +use \BO\Zmsentities\Process; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; class AppointmentUpdate extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $request = $request instanceof ServerRequestInterface ? $request : null; @@ -42,7 +43,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $reservedProcess['clients'][0]['telephone'] = $telephone; $reservedProcess['customTextfield'] = $customTextfield; - $updatedProcess = ZmsApiFacadeService::updateClientData($reservedProcess); + $updatedProcess = ZmsApiFacadeService::updateClientData(new Process($reservedProcess)); if (isset($updatedProcess['exception']) && $updatedProcess['exception'] === 'tooManyAppointmentsWithSameMail') { return $this->createJsonResponse($response, ExceptionService::tooManyAppointmentsWithSameMail(), 406); diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php index 105e823ea..6f057f8a6 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php @@ -9,7 +9,7 @@ class AvailableAppointmentsList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $queryParams = $request->getQueryParams(); diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php index b614eb978..40135c886 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php @@ -10,7 +10,7 @@ class AvailableDaysList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $queryParams = $request->getQueryParams(); diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 85bb384b2..ae93f3d50 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -7,7 +7,7 @@ abstract class BaseController extends \BO\Slim\Controller { - public function __invoke(RequestInterface $request, ResponseInterface $response, array $args) + public function __invoke(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $request = $this->initRequest($request); $noCacheResponse = \BO\Slim\Render::withLastModified($response, time(), '0'); @@ -23,7 +23,7 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, * @param array $args Route parameters * @return ResponseInterface The modified response */ - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface|null { return parent::__invoke($request, $response, $args); } diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index f101cc9dd..35350906c 100644 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -9,7 +9,7 @@ class CaptchaGet extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $captchaDetails = FriendlyCaptchaService::getCaptchaDetails(); diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php index a6efa814a..9a7a552c4 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/UtilityHelper.php @@ -1,6 +1,7 @@ setTimestamp($timestamp); @@ -37,12 +38,12 @@ public static function getInternalDateFromTimestamp(int $timestamp) } } - public static function uniqueElementsFilter($value, $index, $self) + public static function uniqueElementsFilter($value, $index, $self): bool { return array_search($value, $self) === $index; } - public static function getThinnedProcessData(?object $myProcess): array + public static function getThinnedProcessData(?Process $myProcess): array { if (!$myProcess || !isset($myProcess->id)) { return []; diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php index 6df17b579..4e2d39782 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -11,7 +11,7 @@ class OfficesByServiceList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $serviceIdParam = $request->getQueryParams()['serviceId'] ?? []; diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php index 27bb1ed5b..977e7606d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php @@ -9,7 +9,7 @@ class OfficesList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $offices = ZmsApiFacadeService::getOffices(); diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php index 9e89ad4bb..853f1cb59 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php @@ -9,7 +9,7 @@ class OfficesServicesRelations extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $officesAndServices = ZmsApiFacadeService::getServicesAndOffices(); diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php index 7a180bb64..33114dfbd 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -10,7 +10,7 @@ class ScopeByIdGet extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $scopeIdParam = $request->getQueryParams()['scopeId'] ?? []; diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php index e8a8d1319..2e5abd513 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php @@ -9,7 +9,7 @@ class ScopesList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $scopes = ZmsApiFacadeService::getScopes(); diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php index cb52cba27..cafd768b6 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ExceptionService.php @@ -12,7 +12,8 @@ class ExceptionService private const ERROR_APPOINTMENT_NOT_FOUND = 'appointmentNotFound'; private const ERROR_TOO_MANY_APPOINTMENTS = 'tooManyAppointmentsWithSameMail'; - public static function noAppointmentsAtLocation(){ + public static function noAppointmentsAtLocation(): array + { $errors[] = [ 'errorCode' => self::ERROR_NO_APPOINTMENTS, @@ -24,7 +25,8 @@ public static function noAppointmentsAtLocation(){ } - public static function appointmentNotFound(){ + public static function appointmentNotFound(): array + { $errors[] = [ 'errorCode' => self::ERROR_APPOINTMENT_NOT_FOUND, @@ -36,7 +38,8 @@ public static function appointmentNotFound(){ } - public static function tooManyAppointmentsWithSameMail(){ + public static function tooManyAppointmentsWithSameMail(): array + { $errors[] = [ 'errorCode' => self::ERROR_TOO_MANY_APPOINTMENTS, 'errorMessage' => 'Zu viele Termine mit gleicher E-Mail- Adresse.', diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php index 3d527365d..12004612a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php @@ -19,7 +19,7 @@ public static function getCaptchaDetails(): array ]; } - public static function verifyCaptcha(string $solution) + public static function verifyCaptcha(string $solution): bool { try { $response = \App::$http->post(Application::$CAPTCHA_ENDPOINT, [ diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php index c2d0baa97..25f2494cc 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -2,12 +2,16 @@ namespace BO\Zmscitizenapi\Services; +use \BO\Zmsentities\Scope; +use \BO\Zmsentities\Collection\ProviderList; +use \BO\Zmsentities\Collection\RequestList; +use \BO\Zmsentities\Collection\RequestRelationList; use \BO\Zmsentities\Collection\ScopeList; class MapperService { - public static function mapScope($scope) + public static function mapScope(Scope $scope): array { return [ 'id' => $scope['id'] ?? null, @@ -26,7 +30,7 @@ public static function mapScope($scope) ]; } - public static function mapOfficesWithScope($providerList) + public static function mapOfficesWithScope(ProviderList $providerList): array { $offices = []; @@ -47,7 +51,7 @@ public static function mapOfficesWithScope($providerList) return $offices; } - public static function mapServicesWithCombinations($requestList, $relationList) + public static function mapServicesWithCombinations(RequestList $requestList, RequestRelationList $relationList): array { $servicesProviderIds = []; foreach ($relationList as $relation) { @@ -83,7 +87,7 @@ public static function mapServicesWithCombinations($requestList, $relationList) return $services; } - public static function mapRelations($relationList) + public static function mapRelations(RequestRelationList $relationList): array { $relations = []; foreach ($relationList as $relation) { diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 916799979..3090441d0 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -2,10 +2,7 @@ namespace BO\Zmscitizenapi\Services; -use \BO\Mellon\Validator; - use \BO\Zmsentities\Process; -use \BO\Zmsentities\Scope; use \BO\Zmsentities\Collection\ProcessList; use \BO\Zmsentities\Collection\ScopeList; @@ -310,7 +307,7 @@ public static function validateNoAppointmentsAtLocation(): array } - public static function validateUpdateAppointmentInputs($processId, $authKey, $familyName, $email, $telephone, $customTextfield) + public static function validateUpdateAppointmentInputs(?int $processId, ?string $authKey, ?string $familyName, ?string $email, ?string $telephone, ?string $customTextfield): array { $errors = []; @@ -349,7 +346,7 @@ public static function validateUpdateAppointmentInputs($processId, $authKey, $fa ]; } - if ($customTextfield !== null && !is_string($customTextfield)) { + if ($customTextfield !== null && (!is_string($customTextfield) || is_numeric($customTextfield))) { $errors[] = [ 'status' => 400, 'errorMessage' => 'customTextfield should be a string.', diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index aa05bfa53..e404e66f8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -2,82 +2,91 @@ namespace BO\Zmscitizenapi\Services; -use BO\Zmsentities\Calendar as CalendarEntity; -use BO\Zmsentities\Process as ProcessEntity; +use BO\Zmsentities\Calendar as Calendar; +use BO\Zmsentities\Process as Process; +use \BO\Zmsentities\Collection\ProcessList; +use \BO\Zmsentities\Collection\ProviderList; +use \BO\Zmsentities\Collection\RequestList; +use \BO\Zmsentities\Collection\RequestRelationList; +use \BO\Zmsentities\Collection\ScopeList; class ZmsApiClientService { - public static function getOffices() + /* Todo add cache methods + haveCachedSourcesExpired + getSources + + */ + + public static function getOffices(): ProviderList { $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); - $providerList = $sources->getProviderList() ?? []; + $providerList = new ProviderList(); + $providerList = $sources->getProviderList() ?? $providerList; return $providerList; } - public static function getScopes() + public static function getScopes(): ScopeList { $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); - $scopeList = $sources->getScopeList() ?? []; + $scopeList = new ScopeList(); + $scopeList = $sources->getScopeList() ?? $scopeList; return $scopeList; } - public static function getServices() + public static function getServices(): RequestList { $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); - $requestList = $sources->getRequestList() ?? []; + $requestList = new RequestList(); + $requestList = $sources->getRequestList() ?? $requestList; return $requestList; } - public static function getRequestRelationList() + public static function getRequestRelationList(): RequestRelationList { $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); - $requestRelationList = $sources->getRequestRelationList(); + $requestRelationList = new RequestRelationList(); + $requestRelationList = $sources->getRequestRelationList() ?? $requestRelationList; return $requestRelationList; } - - /* Todo add cache methods - haveCachedSourcesExpired - getSources - - */ - - public static function getFreeDays($providers, $requests, $firstDay, $lastDay) + public static function getFreeDays(ProviderList $providers, RequestList $requests, array $firstDay, array $lastDay): Calendar { - - $calendar = new CalendarEntity(); + $calendar = new Calendar(); $calendar->firstDay = $firstDay; $calendar->lastDay = $lastDay; $calendar->providers = $providers; $calendar->requests = $requests; - return \App::$http->readPostResult('/calendar/', $calendar)->getEntity(); + $result = new Calendar(); + $result = \App::$http->readPostResult('/calendar/', $calendar)->getEntity() ?? $result; + return $result; } - public static function getFreeTimeslots($providers, $requests, $firstDay, $lastDay) + public static function getFreeTimeslots(ProviderList $providers, RequestList $requests, array $firstDay, array $lastDay): ProcessList { - $calendar = new CalendarEntity(); + $calendar = new Calendar(); $calendar->firstDay = $firstDay; $calendar->lastDay = $lastDay; $calendar->providers = $providers; @@ -88,13 +97,10 @@ public static function getFreeTimeslots($providers, $requests, $firstDay, $lastD if (!$result || !method_exists($result, 'getCollection')) { throw new \Exception('Invalid response from API'); } - - $psr7Response = $result->getResponse(); - - return $result; + return $result->getCollection(); } - public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts) + public static function reserveTimeslot(Process $appointmentProcess, array $serviceIds, array $serviceCounts): Process { $requests = []; @@ -108,8 +114,7 @@ public static function reserveTimeslot($appointmentProcess, $serviceIds, $servic } } - $processEntity = new ProcessEntity(); - + $processEntity = new Process(); $processEntity->appointments = $appointmentProcess['appointments'] ?? []; $processEntity->authKey = $appointmentProcess['authKey'] ?? null; $processEntity->clients = $appointmentProcess['clients'] ?? []; @@ -118,7 +123,6 @@ public static function reserveTimeslot($appointmentProcess, $serviceIds, $servic $processEntity->requests = $requests; $processEntity->lastChange = $appointmentProcess['lastChange'] ?? time(); - $processEntity->createIP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; $processEntity->createTimestamp = time(); @@ -126,14 +130,15 @@ public static function reserveTimeslot($appointmentProcess, $serviceIds, $servic $processEntity->queue = $appointmentProcess['queue']; } + $result = new Process(); $result = \App::$http->readPostResult('/process/status/reserved/', $processEntity); return $result->getEntity(); } - public static function submitClientData($process) + public static function submitClientData(Process $process): Process|array { - $processEntity = new ProcessEntity(); + $processEntity = new Process(); $processEntity->id = $process['data']['processId'] ?? null; $processEntity->authKey = $process['data']['authKey'] ?? null; $processEntity->appointments = $process['appointments'] ?? []; @@ -161,50 +166,52 @@ public static function submitClientData($process) 'exception' => 'tooManyAppointmentsWithSameMail' ]; return $exception; + } else { + throw $e; } } - } - public function preconfirmProcess($process) + public function preconfirmProcess(?Process $process): Process { $url = '/process/status/preconfirmed/'; return \App::$http->readPostResult($url, $process)->getEntity(); } - public function confirmProcess($process) + public function confirmProcess(?Process $process): Process { $url = '/process/status/confirmed/'; return \App::$http->readPostResult($url, $process)->getEntity(); } - public function cancelAppointment($process) + public function cancelAppointment(?Process $process): Process { $url = "/process/{$process['id']}/{$process['authKey']}/"; return \App::$http->readDeleteResult($url, $process)->getEntity(); } - public function sendConfirmationEmail($process) + public function sendConfirmationEmail(?Process $process): Process { $url = "/process/{$process['id']}/{$process['authKey']}/confirmation/mail/"; return \App::$http->readPostResult($url, $process)->getEntity(); } - public function sendPreconfirmationEmail($process) + public function sendPreconfirmationEmail(?Process $process): Process { $url = "/process/{$process['id']}/{$process['authKey']}/preconfirmation/mail/"; return \App::$http->readPostResult($url, $process)->getEntity(); } - public function sendCancelationEmail($process) + public function sendCancelationEmail(?Process $process): Process { $url = "/process/{$process['id']}/{$process['authKey']}/delete/mail/"; return \App::$http->readPostResult($url, $process)->getEntity(); } - public static function getProcessById($processId, $authKey) + public static function getProcessById(?int $processId, ?string $authKey): Process { $resolveReferences = 2; + $process = new Process(); $process = \App::$http->readGetResult("/process/{$processId}/{$authKey}/", [ 'resolveReferences' => $resolveReferences ])->getEntity(); diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index e2d034270..d3f85f9d8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -6,6 +6,9 @@ use \BO\Zmsentities\Process; use \BO\Zmsentities\Scope; use \BO\Zmsentities\Collection\ScopeList; +use \BO\Zmsentities\Collection\ProviderList; +use \BO\Zmsentities\Collection\RequestList; +use \BO\Zmsentities\Collection\ProcessList; class ZmsApiFacadeService { @@ -14,15 +17,15 @@ public static function getOffices(): array { $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); $providerProjectionList = []; - + foreach (ZmsApiClientService::getOffices() as $provider) { $matchingScope = $scopeList->withProviderID($provider->source, $provider->id)->getIterator()->current(); - + $providerData = [ "id" => $provider->id, "name" => $provider->displayName ?? $provider->name, ]; - + if ($matchingScope instanceof Scope) { $providerData["scope"] = [ "id" => $matchingScope->id, @@ -37,21 +40,21 @@ public static function getOffices(): array "displayInfo" => $matchingScope->getDisplayInfo() ]; } - + $providerProjectionList[] = $providerData; } - + return [ "offices" => $providerProjectionList, "status" => 200 ]; } - + public static function getScopes(): array { $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); $scopesProjectionList = []; - + foreach ($scopeList as $scope) { $scopesProjectionList[] = [ "id" => $scope->id, @@ -66,13 +69,13 @@ public static function getScopes(): array "displayInfo" => $scope->getDisplayInfo() ]; } - + return [ "scopes" => $scopesProjectionList, "status" => 200 ]; } - + public static function getServices(): array { @@ -94,17 +97,17 @@ public static function getServices(): array ]; } - public static function getScopeForProvider(?int $providerId, ?ScopeList $scopes): array + public static function getScopeForProvider(int $providerId, ?ScopeList $scopes): array { - $filteredScopes = $scopes->withProviderID(\App::$source_name, (string)$providerId); - + $filteredScopes = $scopes->withProviderID(\App::$source_name, (string) $providerId); + if ($filteredScopes->count() === 0) { $errors = ValidationService::validateScopesNotFound($filteredScopes); if (!empty($errors['errors'])) { return $errors; } } - + $result = []; foreach ($filteredScopes as $scope) { $result = [ @@ -120,13 +123,13 @@ public static function getScopeForProvider(?int $providerId, ?ScopeList $scopes) "displayInfo" => $scope->getDisplayInfo(), ]; } - + return [ 'scope' => $result, 'status' => 200, ]; } - + public static function getServicesAndOffices(): array { $providerList = ZmsApiClientService::getOffices() ?? []; @@ -152,32 +155,31 @@ public static function getServicesAndOffices(): array * */ - public static function getScopeByOfficeId($officeId): array - { - $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); - $matchingScope = $scopeList->withProviderID('dldb', $officeId)->getIterator()->current(); - - if ($matchingScope instanceof Scope) { - return [ - "id" => $matchingScope->id, - "provider" => $matchingScope->provider, - "shortName" => $matchingScope->shortName, - "telephoneActivated" => $matchingScope->getTelephoneActivated(), - "telephoneRequired" => $matchingScope->getTelephoneRequired(), - "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), - "displayInfo" => $matchingScope->getDisplayInfo(), - ]; - } - - return [ - 'error' => 'Scope not found for the provided office ID', - 'status' => 404, - ]; - } - + public static function getScopeByOfficeId(int $officeId): array + { + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); + $matchingScope = $scopeList->withProviderID('dldb', $officeId)->getIterator()->current(); + + if ($matchingScope instanceof Scope) { + return [ + "id" => $matchingScope->id, + "provider" => $matchingScope->provider, + "shortName" => $matchingScope->shortName, + "telephoneActivated" => $matchingScope->getTelephoneActivated(), + "telephoneRequired" => $matchingScope->getTelephoneRequired(), + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), + "displayInfo" => $matchingScope->getDisplayInfo(), + ]; + } + + return [ + 'error' => 'Scope not found for the provided office ID', + 'status' => 404, + ]; + } /* Todo add method * getOfficeByIds @@ -247,26 +249,26 @@ public static function getOfficesByServiceIds(array $serviceIds): array public static function getScopeByIds(array $scopeIds): array { $scopeIds = array_unique($scopeIds); - + $errors = ValidationService::validateGetScopeByIds($scopeIds); if (!empty($errors['errors'])) { return $errors; } - + $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); $filteredScopes = new ScopeList(); - + foreach ($scopeList as $scope) { if (in_array($scope->id, $scopeIds)) { $filteredScopes->addEntity($scope); } } - + $errors = ValidationService::validateScopesNotFound($filteredScopes); if (!empty($errors['errors'])) { return $errors; } - + $scopes = []; foreach ($filteredScopes as $scope) { $scopes[] = [ @@ -282,7 +284,7 @@ public static function getScopeByIds(array $scopeIds): array "displayInfo" => $scope->getDisplayInfo(), ]; } - + return [ 'scopes' => $scopes, 'status' => 200, @@ -397,14 +399,14 @@ public static function getBookableFreeDays(array $queryParams): array $lastDay = UtilityHelper::getInternalDateFromISO($endDate); $freeDays = ZmsApiClientService::getFreeDays( - [['id' => $officeId, 'source' => 'dldb']], - [ + new ProviderList([['id' => $officeId, 'source' => 'dldb']]), + new RequestList([ [ 'id' => $serviceId, 'source' => 'dldb', 'slotCount' => $serviceCounts, ] - ], + ]), $firstDay, $lastDay, ); @@ -430,7 +432,7 @@ public static function getBookableFreeDays(array $queryParams): array return ExceptionService::noAppointmentsAtLocation(); } } - public static function getFreeAppointments(array $params): array + public static function getFreeAppointments(array $params): mixed { $office = [ 'id' => $params['officeId'], @@ -451,23 +453,19 @@ public static function getFreeAppointments(array $params): array try { $freeSlots = ZmsApiClientService::getFreeTimeslots( - [$office], - $requests, + new ProviderList([$office]), + new RequestList($requests), $params['date'], $params['date'] ); - $psr7Response = $freeSlots->getResponse(); - $responseBody = (string) $psr7Response->getBody(); - $responseBody = json_decode($responseBody, true); - - return $responseBody['data']; + return $freeSlots; } catch (\Exception $e) { return [ 'appointmentTimestamps' => [], 'errorCode' => 'internalError', - 'errorMessage' => 'An error occurred while fetching available appointments', + 'errorMessage' => 'An error occurred while fetching available free appointments', 'status' => 500, ]; } @@ -498,18 +496,15 @@ public static function getAvailableAppointments(array $queryParams): array } } + $freeSlots = new ProcessList(); $freeSlots = ZmsApiClientService::getFreeTimeslots( - [['id' => $officeId, 'source' => 'dldb']], - $requests, + new ProviderList([['id' => $officeId, 'source' => 'dldb']]), + new RequestList($requests), UtilityHelper::getInternalDateFromISO($date), UtilityHelper::getInternalDateFromISO($date) ); - if (!$freeSlots || !method_exists($freeSlots, 'getCollection')) { - throw new \Exception('Invalid response from API'); - } - - return self::processFreeSlots($freeSlots->getCollection()); + return self::processFreeSlots($freeSlots); } catch (\Exception $e) { return [ @@ -521,7 +516,7 @@ public static function getAvailableAppointments(array $queryParams): array } } - private static function processFreeSlots($freeSlots): array + private static function processFreeSlots(ProcessList $freeSlots): array { $errors = ValidationService::validateGetProcessFreeSlots($freeSlots); @@ -562,7 +557,7 @@ private static function processFreeSlots($freeSlots): array ]; } - public static function reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts): Process + public static function reserveTimeslot(Process $appointmentProcess, array $serviceIds, array $serviceCounts): Process { return ZmsApiClientService::reserveTimeslot($appointmentProcess, $serviceIds, $serviceCounts); } @@ -595,16 +590,16 @@ public static function getProcessById(?int $processId, ?string $authKey): array } } - public static function updateClientData($reservedProcess): Process|array + public static function updateClientData(Process $reservedProcess): Process|array { $clientUpdateResult = ZmsApiClientService::submitClientData($reservedProcess); - + if (isset($clientUpdateResult['error'])) { return $clientUpdateResult; } - + return $clientUpdateResult; - } + } /* Todo add method * preconfirmAppointment diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php index 1c1812758..0bb91b0b0 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php @@ -10,7 +10,7 @@ class ServicesByOfficeList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $officeIds = explode(',', $request->getQueryParams()['officeId'] ?? ''); diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php index a57adc64f..263a6e2dc 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php @@ -9,7 +9,7 @@ class ServicesList extends BaseController { - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args) + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $services = ZmsApiFacadeService::getServices(); From c3ccbb4396a40a983d51d97d43d0a0bce47759eb Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 12:02:20 +0100 Subject: [PATCH 31/42] clean(MPDZBS-877): typing overhaul using zmsentities --- zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index e404e66f8..a083e602c 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -70,6 +70,7 @@ public static function getRequestRelationList(): RequestRelationList return $requestRelationList; } + public static function getFreeDays(ProviderList $providers, RequestList $requests, array $firstDay, array $lastDay): Calendar { $calendar = new Calendar(); From e9dac7ddaa49b7f501f6d2d9930de6d06882f84f Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 12:04:28 +0100 Subject: [PATCH 32/42] clean(MPDZBS-877): improve telephone validation regex --- zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 3090441d0..e272bb203 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -339,7 +339,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string ]; } - if ($telephone !== null && !$telephone || !preg_match('/^\d{7,15}$/', $telephone)) { + if ($telephone !== null && (!$telephone || !preg_match('/^\d{7,15}$/', $telephone))) { $errors[] = [ 'status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.', From 6724f37e64890aeb3417967307e1bbd281b94925 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 12:05:32 +0100 Subject: [PATCH 33/42] clean(MPDZBS-877): improve serviceId validation regex --- zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index e272bb203..221c5b434 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -95,7 +95,7 @@ public static function validateGetAvailableAppointments(?string $date, ?int $off ]; } - if (empty($serviceIds[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceIds))) { + if (empty($serviceIds) || !is_array($serviceIds) || array_filter($serviceIds, fn($id) => !is_numeric($id))) { $errors[] = [ 'status' => 400, 'errorMessage' => 'serviceId should be a comma-separated string of integers.', From 1a27b172da4f7289c6cc58fc032fd5cb8a5ea470 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 12:06:32 +0100 Subject: [PATCH 34/42] clean(MPDZBS-877): fix naming issue --- .../src/Zmscitizenapi/Services/ValidationService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 221c5b434..33536ddd2 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -377,11 +377,11 @@ public static function validateServiceIdParam(array $serviceIds): array return empty($errors) ? [] : $errors; } - public static function validateScopeIdParam(array $serviceIds): array + public static function validateScopeIdParam(array $scopeIds): array { $errors = []; - foreach ($serviceIds as $id) { + foreach ($scopeIds as $id) { if (!is_numeric($id)) { $errors[] = [ 'scopes' => [], From cb8fcb9518e6f9b743b690e41664c532fe64553e Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 12:13:36 +0100 Subject: [PATCH 35/42] clean(MPDZBS-877): make hardcoded source name dynamic --- zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php | 1 + .../src/Zmscitizenapi/Services/ZmsApiFacadeService.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php index 1e8ae6bf1..f42ec7efd 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php @@ -4,6 +4,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Bo\Slim\Response; use Slim\Interfaces\ErrorHandlerInterface; use Throwable; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index d3f85f9d8..f40660e2b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -158,7 +158,7 @@ public static function getServicesAndOffices(): array public static function getScopeByOfficeId(int $officeId): array { $scopeList = new ScopeList(ZmsApiClientService::getScopes() ?? []); - $matchingScope = $scopeList->withProviderID('dldb', $officeId)->getIterator()->current(); + $matchingScope = $scopeList->withProviderID(\App::$source_name, $officeId)->getIterator()->current(); if ($matchingScope instanceof Scope) { return [ From 60553d2512f7d7a80ec1793d48fb09a3d925db81 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 16:09:12 +0100 Subject: [PATCH 36/42] clean(MPDZBS-877): clean up more and change citizenapi to utf8 --- .../src/Zmscitizenapi/AppointmentReserve.php | 47 +++++++++---------- .../src/Zmscitizenapi/BaseController.php | 21 +++++++-- .../src/Zmscitizenapi/Helper/ErrorHandler.php | 3 +- .../Zmscitizenapi/Services/MapperService.php | 22 ++++----- .../Services/ValidationService.php | 3 +- .../Services/ZmsApiClientService.php | 2 +- .../Services/ZmsApiFacadeService.php | 12 ++--- 7 files changed, 62 insertions(+), 48 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 0d85ceb8b..38e0408bd 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -21,24 +21,24 @@ class AppointmentReserve extends BaseController public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { $request = $request instanceof ServerRequestInterface ? $request : null; - + $body = $request->getParsedBody(); - + $officeId = isset($body['officeId']) && is_numeric($body['officeId']) ? (int) $body['officeId'] : null; $serviceIds = $body['serviceId'] ?? null; $serviceCounts = $body['serviceCount'] ?? [1]; $captchaSolution = $body['captchaSolution'] ?? null; $timestamp = isset($body['timestamp']) && is_numeric($body['timestamp']) ? (int) $body['timestamp'] : null; - + $errors = ValidationService::validatePostAppointmentReserve($officeId, $serviceIds, $serviceCounts, $timestamp); if (!empty($errors['errors'])) { return $this->createJsonResponse($response, $errors, 400); } - + try { $providerScope = ZmsApiFacadeService::getScopeByOfficeId($officeId); - $captchaRequired = Application::$CAPTCHA_ENABLED === true && $providerScope['captchaActivatedRequired'] === "1"; - + $captchaRequired = Application::$CAPTCHA_ENABLED === true && isset($providerScope['captchaActivatedRequired']) && $providerScope['captchaActivatedRequired'] === "1"; + if ($captchaRequired) { $captchaVerificationResult = FriendlyCaptchaService::verifyCaptcha($captchaSolution); if (!$captchaVerificationResult['success']) { @@ -48,12 +48,12 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo ], 400); } } - + $serviceValidationResult = ValidationService::validateServiceLocationCombination($officeId, $serviceIds); if ($serviceValidationResult['status'] !== 200) { return $this->createJsonResponse($response, $serviceValidationResult, 400); } - + $freeAppointments = new ProcessList(); $freeAppointments = ZmsApiFacadeService::getFreeAppointments([ 'officeId' => $officeId, @@ -61,7 +61,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo 'serviceCounts' => $serviceCounts, 'date' => UtilityHelper::getInternalDateFromTimestamp($timestamp) ]); - + $processArray = json_decode(json_encode($freeAppointments), true); $filteredProcesses = array_filter($processArray, function ($process) use ($timestamp) { @@ -70,47 +70,46 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo } return in_array($timestamp, array_column($process['appointments'], 'date')); }); - - $selectedProcess = null; - + + $selectedProcess = $filteredProcesses ? new Process() : null; + if (!empty($filteredProcesses)) { $selectedProcessData = array_values($filteredProcesses)[0]; - - $selectedProcess = new Process(); + $scopeData = $selectedProcessData['scope'] ?? null; $scope = $scopeData ? new Scope($scopeData) : null; - + $selectedProcess->withUpdatedData($selectedProcessData, new \DateTime("@$timestamp"), $scope); - } - + } + $errors = ValidationService::validateGetProcessNotFound($selectedProcess); if (!empty($errors['errors'])) { return $this->createJsonResponse($response, $errors, 404); } - + $selectedProcess->clients = [ [ 'email' => 'default@example.com' ] ]; - + $reservedProcess = new Process(); $reservedProcess = ZmsApiFacadeService::reserveTimeslot($selectedProcess, $serviceIds, $serviceCounts); - + if ($reservedProcess && $reservedProcess->scope && $reservedProcess->scope->id) { $scopeIds = [$reservedProcess->scope->id]; $scopesData = ZmsApiFacadeService::getScopeByIds($scopeIds); - + if ($scopesData['status'] === 200 && isset($scopesData['scopes']['scopes']) && !empty($scopesData['scopes']['scopes'])) { $reservedProcess->scope = MapperService::mapScope($scopesData['scopes']['scopes'][0]); } } - + $thinnedProcessData = UtilityHelper::getThinnedProcessData($reservedProcess); $thinnedProcessData = array_merge($thinnedProcessData, ['officeId' => $officeId]); - + return $this->createJsonResponse($response, $thinnedProcessData, 200); - + } catch (\Exception $e) { return $this->createJsonResponse($response, [ 'errorCode' => 'internalServerError', diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index ae93f3d50..05385b699 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -23,18 +23,31 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, * @param array $args Route parameters * @return ResponseInterface The modified response */ - public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface|null + public function readResponse(RequestInterface $request, ResponseInterface $response, array $args): ResponseInterface { return parent::__invoke($request, $response, $args); } protected function createJsonResponse(ResponseInterface $response, array $content, int $statusCode): ResponseInterface { + if ($statusCode < 100 || $statusCode > 599) { + throw new \InvalidArgumentException('Invalid HTTP status code'); + } + $response = $response->withStatus($statusCode) - ->withHeader('Content-Type', 'application/json'); - $response->getBody()->write(json_encode($content)); + ->withHeader('Content-Type', 'application/json; charset=utf-8'); + + try { + $json = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + } catch (\JsonException $e) { + throw new \RuntimeException('Failed to encode JSON response: ' . $e->getMessage(), 0, $e); + } + + $response->getBody()->write($json); + return $response; } - + + } \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php index f42ec7efd..7f60aa440 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php +++ b/zmscitizenapi/src/Zmscitizenapi/Helper/ErrorHandler.php @@ -4,7 +4,8 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Bo\Slim\Response; +use BO\Slim\Response; +use Slim\Exception\HttpException; use Slim\Interfaces\ErrorHandlerInterface; use Throwable; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php index 25f2494cc..e24c6d4ff 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -14,19 +14,19 @@ class MapperService public static function mapScope(Scope $scope): array { return [ - 'id' => $scope['id'] ?? null, + 'id' => $scope->id ?? null, 'provider' => [ - 'id' => $scope['provider']['id'] ?? null, - 'source' => $scope['provider']['source'] ?? null, + 'id' => $scope->provider->id ?? null, + 'source' => $scope->provider->source ?? null, ], - 'shortName' => $scope['shortName'] ?? null, - 'telephoneActivated' => $scope['telephoneActivated'] ?? null, - 'telephoneRequired' => $scope['telephoneRequired'] ?? null, - 'customTextfieldActivated' => $scope['customTextfieldActivated'] ?? null, - 'customTextfieldRequired' => $scope['customTextfieldRequired'] ?? null, - 'customTextfieldLabel' => $scope['customTextfieldLabel'] ?? null, - 'captchaActivatedRequired' => $scope['captchaActivatedRequired'] ?? null, - 'displayInfo' => $scope['displayInfo'] ?? null, + 'shortName' => $scope->shortName ?? null, + 'telephoneActivated' => $scope->getTelephoneActivated() ?? null, + 'telephoneRequired' => $scope->getTelephoneRequired() ?? null, + 'customTextfieldActivated' => $scope->getCustomTextfieldActivated() ?? null, + 'customTextfieldRequired' => $scope->getCustomTextfieldRequired() ?? null, + 'customTextfieldLabel' => $scope->getCustomTextfieldLabel() ?? null, + 'captchaActivatedRequired' => $scope->getCaptchaActivatedRequired() ?? null, + 'displayInfo' => $scope->getDisplayInfo() ?? null, ]; } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 33536ddd2..0a51e6f13 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -234,9 +234,10 @@ public static function validateGetProcessNotFound(?Process $process): array 'status' => 404, ]; } - + return ['errors' => $errors, 'status' => 404]; } + public static function validateScopesNotFound(?ScopeList $scopes): array { diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index a083e602c..287e2b4af 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -110,7 +110,7 @@ public static function reserveTimeslot(Process $appointmentProcess, array $servi for ($i = 0; $i < $count; $i++) { $requests[] = [ 'id' => $serviceId, - 'source' => 'dldb' + 'source' => \App::$source_name ]; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index f40660e2b..e8cb52cac 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -399,11 +399,11 @@ public static function getBookableFreeDays(array $queryParams): array $lastDay = UtilityHelper::getInternalDateFromISO($endDate); $freeDays = ZmsApiClientService::getFreeDays( - new ProviderList([['id' => $officeId, 'source' => 'dldb']]), + new ProviderList([['id' => $officeId, 'source' => \App::$source_name]]), new RequestList([ [ 'id' => $serviceId, - 'source' => 'dldb', + 'source' => \App::$source_name, 'slotCount' => $serviceCounts, ] ]), @@ -436,7 +436,7 @@ public static function getFreeAppointments(array $params): mixed { $office = [ 'id' => $params['officeId'], - 'source' => 'dldb' + 'source' => \App::$source_name ]; $requests = []; @@ -444,7 +444,7 @@ public static function getFreeAppointments(array $params): mixed foreach ($params['serviceIds'] as $index => $serviceId) { $service = [ 'id' => $serviceId, - 'source' => 'dldb', + 'source' => \App::$source_name, 'slotCount' => $params['serviceCounts'][$index] ]; $requests = array_merge($requests, array_fill(0, $service['slotCount'], $service)); @@ -490,7 +490,7 @@ public static function getAvailableAppointments(array $queryParams): array for ($i = 0; $i < $slotCount; $i++) { $requests[] = [ 'id' => $serviceId, - 'source' => 'dldb', + 'source' => \App::$source_name, 'slotCount' => 1, ]; } @@ -498,7 +498,7 @@ public static function getAvailableAppointments(array $queryParams): array $freeSlots = new ProcessList(); $freeSlots = ZmsApiClientService::getFreeTimeslots( - new ProviderList([['id' => $officeId, 'source' => 'dldb']]), + new ProviderList([['id' => $officeId, 'source' => \App::$source_name]]), new RequestList($requests), UtilityHelper::getInternalDateFromISO($date), UtilityHelper::getInternalDateFromISO($date) From 1b4dbd9e32b205f89b48a03229a883c3f7924d5d Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 19:03:14 +0100 Subject: [PATCH 37/42] clean(MPDZBS-877): improve error messaging codes and expand office api with geocoordinates and address --- .../src/Zmscitizenapi/BaseController.php | 15 +- .../Zmscitizenapi/Services/MapperService.php | 20 +- .../Services/ValidationService.php | 66 ++- .../Services/ZmsApiClientService.php | 2 + .../Services/ZmsApiFacadeService.php | 103 ++--- .../Zmscitizenapi/AppointmentGetTest.php | 6 + .../Zmscitizenapi/AppointmentReserveTest.php | 31 +- .../Zmscitizenapi/AppointmentUpdateTest.php | 384 +++++++++--------- .../AvailableAppointmentsListTest.php | 24 +- .../Zmscitizenapi/AvailableDaysListTest.php | 39 ++ .../OfficesByServiceListTest.php | 8 +- .../tests/Zmscitizenapi/ScopeByIdGetTest.php | 6 +- .../ServicesByOfficeListTest.php | 3 +- zmsentities/src/Zmsentities/Scope.php | 5 + 14 files changed, 415 insertions(+), 297 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/BaseController.php b/zmscitizenapi/src/Zmscitizenapi/BaseController.php index 05385b699..e9c4c07ae 100644 --- a/zmscitizenapi/src/Zmscitizenapi/BaseController.php +++ b/zmscitizenapi/src/Zmscitizenapi/BaseController.php @@ -33,21 +33,20 @@ protected function createJsonResponse(ResponseInterface $response, array $conten if ($statusCode < 100 || $statusCode > 599) { throw new \InvalidArgumentException('Invalid HTTP status code'); } - + $response = $response->withStatus($statusCode) ->withHeader('Content-Type', 'application/json; charset=utf-8'); - + try { - $json = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + // Add JSON_UNESCAPED_SLASHES to ensure slashes in HTML are not escaped + $json = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } catch (\JsonException $e) { throw new \RuntimeException('Failed to encode JSON response: ' . $e->getMessage(), 0, $e); } - + $response->getBody()->write($json); - + return $response; } - - - + } \ No newline at end of file diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php index e24c6d4ff..7b622d134 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -15,11 +15,8 @@ public static function mapScope(Scope $scope): array { return [ 'id' => $scope->id ?? null, - 'provider' => [ - 'id' => $scope->provider->id ?? null, - 'source' => $scope->provider->source ?? null, - ], - 'shortName' => $scope->shortName ?? null, + "provider" => $scope->getProvider() ?? null, + "shortName" => $scope->getShortName() ?? null, 'telephoneActivated' => $scope->getTelephoneActivated() ?? null, 'telephoneRequired' => $scope->getTelephoneRequired() ?? null, 'customTextfieldActivated' => $scope->getCustomTextfieldActivated() ?? null, @@ -37,10 +34,15 @@ public static function mapOfficesWithScope(ProviderList $providerList): array $scopes = new ScopeList(ZmsApiClientService::getScopes() ?? []); foreach ($providerList as $provider) { - $officeData = [ - "id" => $provider->id, - "name" => $provider->displayName ?? $provider->name, - ]; + $officeData = array_merge( + [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ], + !empty($provider->data['address']) ? ["address" => $provider->data['address']] : [], + !empty($provider->data['geo']) ? ["geo" => $provider->data['geo']] : [] + ); + $providerScope = ZmsApiFacadeService::getScopeForProvider($provider->id, $scopes); if (isset($providerScope['scope'])) { $officeData['scope'] = $providerScope['scope']; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 0a51e6f13..18b08a1d9 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -40,19 +40,19 @@ public static function validateGetBookableFreeDays(?int $officeId, ?int $service { $errors = []; if (!$startDate) { - $errors[] = ['status' => 400, 'errorMessage' => 'startDate is required and must be a valid date.']; + $errors[] = ['status' => 400, 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.']; } if (!$endDate) { - $errors[] = ['status' => 400, 'errorMessage' => 'endDate is required and must be a valid date.']; + $errors[] = ['status' => 400, 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.']; } if (!$officeId || !is_numeric($officeId)) { - $errors[] = ['status' => 400, 'errorMessage' => 'officeId should be a 32-bit integer.']; + $errors[] = ['status' => 400, 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.']; } if (!$serviceId || !is_numeric($serviceId)) { - $errors[] = ['status' => 400, 'errorMessage' => 'serviceId should be a 32-bit integer.']; + $errors[] = ['status' => 400, 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.']; } if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { - $errors[] = ['status' => 400, 'errorMessage' => 'serviceCount should be a comma-separated string of integers.']; + $errors[] = ['status' => 400, 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.']; } return ['errors' => $errors, 'status' => 400]; @@ -64,6 +64,7 @@ public static function validateGetProcessById(?int $processId, ?string $authKey) if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a 32-bit integer.', ]; } @@ -71,6 +72,7 @@ public static function validateGetProcessById(?int $processId, ?string $authKey) if (!$authKey || !is_string($authKey)) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a string.', ]; } @@ -84,6 +86,7 @@ public static function validateGetAvailableAppointments(?string $date, ?int $off if (!$date) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidDate', 'errorMessage' => 'date is required and must be a valid date.', ]; } @@ -91,6 +94,7 @@ public static function validateGetAvailableAppointments(?string $date, ?int $off if (!$officeId || !is_numeric($officeId)) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ]; } @@ -98,13 +102,15 @@ public static function validateGetAvailableAppointments(?string $date, ?int $off if (empty($serviceIds) || !is_array($serviceIds) || array_filter($serviceIds, fn($id) => !is_numeric($id))) { $errors[] = [ 'status' => 400, - 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ]; } if (empty($serviceCounts[0]) || !preg_match('/^\d+(,\d+)*$/', implode(',', $serviceCounts))) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', ]; } @@ -118,6 +124,7 @@ public static function validatePostAppointmentReserve(?int $officeId, ?array $se if (!$officeId || !is_numeric($officeId)) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ]; } @@ -125,18 +132,21 @@ public static function validatePostAppointmentReserve(?int $officeId, ?array $se if (empty($serviceIds)) { $errors[] = [ 'status' => 400, - 'errorMessage' => 'Missing serviceId.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ]; } elseif (!is_array($serviceIds) || array_filter($serviceIds, fn($id) => !is_numeric($id))) { $errors[] = [ 'status' => 400, - 'errorMessage' => 'Invalid serviceId format. It should be an array of numeric values.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ]; } if (!$timestamp || !is_numeric($timestamp) || $timestamp < 0) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidTimestamp', 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ]; } @@ -144,7 +154,8 @@ public static function validatePostAppointmentReserve(?int $officeId, ?array $se if (!is_array($serviceCounts) || array_filter($serviceCounts, fn($count) => !is_numeric($count) || $count < 0)) { $errors[] = [ 'status' => 400, - 'errorMessage' => 'Invalid serviceCount format. It should be an array of non-negative numeric values.', + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', ]; } @@ -157,7 +168,8 @@ public static function validateGetOfficesByServiceIds(?array $serviceIds): array if (empty($serviceIds) || $serviceIds == ['']) { $errors[] = [ 'offices' => [], - 'errorMessage' => 'Invalid serviceId(s).', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400 ]; } @@ -171,7 +183,8 @@ public static function validateGetScopeByIds(?array $scopeIds): array if (empty($scopeIds) || $scopeIds == ['']) { $errors[] = [ 'scopes' => [], - 'errorMessage' => 'Invalid scopeId(s).', + 'errorCode' => 'invalidScopeId', + 'errorMessage' => "scopeId should be a 32-bit integer.", 'status' => 400 ]; } @@ -183,12 +196,16 @@ public static function validateGetServicesByOfficeIds(?array $officeIds): array { $errors = []; - if (empty($officeIds) || $officeIds == ['']) { - $errors[] = [ - 'services' => [], - 'errorMessage' => 'Invalid officeId(s)', - 'status' => 400, - ]; + + foreach ($officeIds as $id) { + if (!is_numeric($id)) { + $errors[] = [ + 'services' => [], + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => "officeId should be a 32-bit integer.", + 'status' => 400, + ]; + } } return ['errors' => $errors, 'status' => 400]; @@ -238,7 +255,6 @@ public static function validateGetProcessNotFound(?Process $process): array return ['errors' => $errors, 'status' => 404]; } - public static function validateScopesNotFound(?ScopeList $scopes): array { $errors = []; @@ -315,6 +331,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string if (!$processId || !is_numeric($processId) || intval($processId) <= 0) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.', ]; } @@ -322,6 +339,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string if (!$authKey || !is_string($authKey)) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.', ]; } @@ -329,6 +347,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string if (!$familyName || !is_string($familyName)) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.', ]; } @@ -336,6 +355,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.', ]; } @@ -343,6 +363,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string if ($telephone !== null && (!$telephone || !preg_match('/^\d{7,15}$/', $telephone))) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.', ]; } @@ -350,6 +371,7 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string if ($customTextfield !== null && (!is_string($customTextfield) || is_numeric($customTextfield))) { $errors[] = [ 'status' => 400, + 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.', ]; } @@ -369,7 +391,8 @@ public static function validateServiceIdParam(array $serviceIds): array if (!is_numeric($id)) { $errors[] = [ 'offices' => [], - 'errorMessage' => "Invalid service ID: $id. Must be a number.", + 'errorCode' => 'invalidServiceId', + 'errorMessage' => "serviceId should be a 32-bit integer.", 'status' => 400, ]; } @@ -381,12 +404,12 @@ public static function validateServiceIdParam(array $serviceIds): array public static function validateScopeIdParam(array $scopeIds): array { $errors = []; - foreach ($scopeIds as $id) { if (!is_numeric($id)) { $errors[] = [ 'scopes' => [], - 'errorMessage' => "Invalid scope ID: $id. Must be a number.", + 'errorCode' => 'invalidScopeId', + 'errorMessage' => "scopeId should be a 32-bit integer.", 'status' => 400, ]; } @@ -396,4 +419,3 @@ public static function validateScopeIdParam(array $scopeIds): array } } - diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index 287e2b4af..d4781ac6d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -4,6 +4,7 @@ use BO\Zmsentities\Calendar as Calendar; use BO\Zmsentities\Process as Process; +use BO\Zmsentities\Collection\SourceList; use \BO\Zmsentities\Collection\ProcessList; use \BO\Zmsentities\Collection\ProviderList; use \BO\Zmsentities\Collection\RequestList; @@ -21,6 +22,7 @@ class ZmsApiClientService public static function getOffices(): ProviderList { + $sources = new SourceList(); $sources = \App::$http->readGetResult('/source/' . \App::$source_name . '/', [ 'resolveReferences' => 2, ])->getEntity(); diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index e8cb52cac..2a7f19a41 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -21,23 +21,27 @@ public static function getOffices(): array foreach (ZmsApiClientService::getOffices() as $provider) { $matchingScope = $scopeList->withProviderID($provider->source, $provider->id)->getIterator()->current(); - $providerData = [ - "id" => $provider->id, - "name" => $provider->displayName ?? $provider->name, - ]; - + $providerData = array_merge( + [ + "id" => $provider->id, + "name" => $provider->displayName ?? $provider->name, + ], + !empty($provider->data['address']) ? ["address" => $provider->data['address']] : [], + !empty($provider->data['geo']) ? ["geo" => $provider->data['geo']] : [] + ); + if ($matchingScope instanceof Scope) { $providerData["scope"] = [ "id" => $matchingScope->id, - "provider" => $matchingScope->provider, - "shortName" => $matchingScope->shortName, - "telephoneActivated" => $matchingScope->getTelephoneActivated(), - "telephoneRequired" => $matchingScope->getTelephoneRequired(), - "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), - "displayInfo" => $matchingScope->getDisplayInfo() + "provider" => $matchingScope->getProvider() ?? null, + "shortName" => $matchingScope->getShortName() ?? null, + "telephoneActivated" => $matchingScope->getTelephoneActivated() ?? null, + "telephoneRequired" => $matchingScope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $matchingScope->getDisplayInfo() ?? null ]; } @@ -58,15 +62,15 @@ public static function getScopes(): array foreach ($scopeList as $scope) { $scopesProjectionList[] = [ "id" => $scope->id, - "provider" => $scope->getProvider(), - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), - "displayInfo" => $scope->getDisplayInfo() + "provider" => $scope->getProvider() ?? null, + "shortName" => $scope->getShortName() ?? null, + "telephoneActivated" => $scope->getTelephoneActivated() ?? null, + "telephoneRequired" => $scope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $scope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $scope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $scope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $scope->getDisplayInfo() ?? null ]; } @@ -113,14 +117,14 @@ public static function getScopeForProvider(int $providerId, ?ScopeList $scopes): $result = [ "id" => $scope->id, "provider" => $scope->getProvider(), - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), - "displayInfo" => $scope->getDisplayInfo(), + "shortName" => $scope->getShortName() ?? null, + "telephoneActivated" => $scope->getTelephoneActivated() ?? null, + "telephoneRequired" => $scope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $scope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $scope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $scope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $scope->getDisplayInfo() ?? null, ]; } @@ -163,15 +167,15 @@ public static function getScopeByOfficeId(int $officeId): array if ($matchingScope instanceof Scope) { return [ "id" => $matchingScope->id, - "provider" => $matchingScope->provider, - "shortName" => $matchingScope->shortName, - "telephoneActivated" => $matchingScope->getTelephoneActivated(), - "telephoneRequired" => $matchingScope->getTelephoneRequired(), - "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired(), - "displayInfo" => $matchingScope->getDisplayInfo(), + "provider" => $matchingScope->getProvider() ?? null, + "shortName" => $matchingScope->getShortName() ?? null, + "telephoneActivated" => $matchingScope->getTelephoneActivated() ?? null, + "telephoneRequired" => $matchingScope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $matchingScope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $matchingScope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $matchingScope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $matchingScope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $matchingScope->getDisplayInfo() ?? null, ]; } @@ -273,15 +277,15 @@ public static function getScopeByIds(array $scopeIds): array foreach ($filteredScopes as $scope) { $scopes[] = [ "id" => $scope->id, - "provider" => $scope->getProvider(), - "shortName" => $scope->shortName, - "telephoneActivated" => $scope->getTelephoneActivated(), - "telephoneRequired" => $scope->getTelephoneRequired(), - "customTextfieldActivated" => $scope->getCustomTextfieldActivated(), - "customTextfieldRequired" => $scope->getCustomTextfieldRequired(), - "customTextfieldLabel" => $scope->getCustomTextfieldLabel(), - "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired(), - "displayInfo" => $scope->getDisplayInfo(), + "provider" => $scope->getProvider() ?? null, + "shortName" => $scope->getShortName() ?? null, + "telephoneActivated" => $scope->getTelephoneActivated() ?? null, + "telephoneRequired" => $scope->getTelephoneRequired() ?? null, + "customTextfieldActivated" => $scope->getCustomTextfieldActivated() ?? null, + "customTextfieldRequired" => $scope->getCustomTextfieldRequired() ?? null, + "customTextfieldLabel" => $scope->getCustomTextfieldLabel() ?? null, + "captchaActivatedRequired" => $scope->getCaptchaActivatedRequired() ?? null, + "displayInfo" => $scope->getDisplayInfo() ?? null, ]; } @@ -432,6 +436,7 @@ public static function getBookableFreeDays(array $queryParams): array return ExceptionService::noAppointmentsAtLocation(); } } + public static function getFreeAppointments(array $params): mixed { $office = [ diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php index 375f9dcac..2e4b60bd8 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentGetTest.php @@ -170,6 +170,7 @@ public function testMissingProcessId() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a 32-bit integer.' ] ], @@ -191,6 +192,7 @@ public function testMissingAuthKey() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a string.' ] ], @@ -213,6 +215,7 @@ public function testInvalidProcessId() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a 32-bit integer.' ] ], @@ -235,6 +238,7 @@ public function testInvalidAuthKey() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a string.' ] ], @@ -254,10 +258,12 @@ public function testBothParametersMissing() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a 32-bit integer.', ], [ 'status' => 400, + 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a string.', ] ], diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php index e57e4bebf..fa0798672 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentReserveTest.php @@ -128,6 +128,7 @@ public function testMissingOfficeId() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ] ], @@ -154,7 +155,8 @@ public function testMissingServiceId() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing serviceId.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ] ], 'status' => 400 @@ -180,6 +182,7 @@ public function testMissingTimestamp() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidTimestamp', 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], @@ -205,11 +208,13 @@ public function testMissingOfficeIdAndServiceId() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ], [ 'status' => 400, - 'errorMessage' => 'Missing serviceId.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ] ], 'status' => 400 @@ -234,10 +239,12 @@ public function testMissingOfficeIdAndTimestamp() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ], [ 'status' => 400, + 'errorCode' => 'invalidTimestamp', 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], @@ -263,10 +270,12 @@ public function testMissingServiceIdAndTimestamp() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing serviceId.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ], [ 'status' => 400, + 'errorCode' => 'invalidTimestamp', 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], @@ -288,14 +297,17 @@ public function testMissingAllFields() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ], [ 'status' => 400, - 'errorMessage' => 'Missing serviceId.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ], [ 'status' => 400, + 'errorCode' => 'invalidTimestamp', 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], @@ -323,6 +335,7 @@ public function testInvalidOfficeIdFormat() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', ] ], @@ -350,7 +363,8 @@ public function testInvalidServiceIdFormat() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Invalid serviceId format. It should be an array of numeric values.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ] ], 'status' => 400 @@ -377,6 +391,7 @@ public function testInvalidTimestampFormat() 'errors' => [ [ 'status' => 400, + 'errorCode' => 'invalidTimestamp', 'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.', ] ], @@ -403,7 +418,8 @@ public function testEmptyServiceIdArray() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Missing serviceId.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', ] ], 'status' => 400 @@ -430,7 +446,8 @@ public function testInvalidServiceCount() 'errors' => [ [ 'status' => 400, - 'errorMessage' => 'Invalid serviceCount format. It should be an array of non-negative numeric values.', + 'errorCode' => 'invalidServiceCount', + 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', ] ], 'status' => 400 diff --git a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php index 5b2013734..057f94b84 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AppointmentUpdateTest.php @@ -168,12 +168,12 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEma $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -197,11 +197,11 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEma $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -225,11 +225,11 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEma $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -253,10 +253,10 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEma $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -280,11 +280,11 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -308,10 +308,10 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -335,10 +335,10 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -362,9 +362,9 @@ public function testInvalidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] ], 'status' => 400 ]; @@ -388,11 +388,11 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -416,10 +416,10 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -443,10 +443,10 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -470,9 +470,9 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -496,10 +496,10 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -523,9 +523,9 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -549,9 +549,9 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -575,8 +575,8 @@ public function testInvalidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'] ], 'status' => 400 ]; @@ -600,11 +600,11 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -628,10 +628,10 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -655,10 +655,10 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -682,9 +682,9 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -708,10 +708,10 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -735,9 +735,9 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -761,9 +761,9 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -787,8 +787,8 @@ public function testInvalidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] ], 'status' => 400 ]; @@ -812,10 +812,10 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -839,9 +839,9 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -865,9 +865,9 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -891,8 +891,8 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -916,9 +916,9 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -942,8 +942,8 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -967,8 +967,8 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -992,7 +992,7 @@ public function testInvalidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'processId should be a positive 32-bit integer.'] + ['status' => 400, 'errorCode' => 'invalidProcessId', 'errorMessage' => 'processId should be a positive 32-bit integer.'] ], 'status' => 400 ]; @@ -1016,11 +1016,11 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1044,10 +1044,10 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1071,10 +1071,10 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1098,9 +1098,9 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_InvalidEmail $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -1124,10 +1124,10 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1151,9 +1151,9 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1177,9 +1177,9 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1203,8 +1203,8 @@ public function testValidProcessid_InvalidAuthkey_InvalidFamilyname_ValidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] ], 'status' => 400 ]; @@ -1228,10 +1228,10 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1255,9 +1255,9 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1281,9 +1281,9 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1307,8 +1307,8 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_InvalidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -1332,9 +1332,9 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1358,8 +1358,8 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1383,8 +1383,8 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1408,7 +1408,7 @@ public function testValidProcessid_InvalidAuthkey_ValidFamilyname_ValidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'authKey should be a non-empty string.'] + ['status' => 400, 'errorCode' => 'invalidAuthKey', 'errorMessage' => 'authKey should be a non-empty string.'] ], 'status' => 400 ]; @@ -1432,10 +1432,10 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1459,9 +1459,9 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_I $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1485,9 +1485,9 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1511,8 +1511,8 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_InvalidEmail_V $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -1536,9 +1536,9 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1562,8 +1562,8 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1587,8 +1587,8 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1612,7 +1612,7 @@ public function testValidProcessid_ValidAuthkey_InvalidFamilyname_ValidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'familyName should be a non-empty string.'] + ['status' => 400, 'errorCode' => 'invalidFamilyName', 'errorMessage' => 'familyName should be a non-empty string.'] ], 'status' => 400 ]; @@ -1636,9 +1636,9 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1662,8 +1662,8 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_Inv $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1687,8 +1687,8 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1712,7 +1712,7 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_InvalidEmail_Val $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'email should be a valid email address.'] + ['status' => 400, 'errorCode' => 'invalidEmail', 'errorMessage' => 'email should be a valid email address.'] ], 'status' => 400 ]; @@ -1736,8 +1736,8 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inval $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'], + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; @@ -1761,7 +1761,7 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Inval $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] + ['status' => 400, 'errorCode' => 'invalidTelephone', 'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'] ], 'status' => 400 ]; @@ -1785,7 +1785,7 @@ public function testValidProcessid_ValidAuthkey_ValidFamilyname_ValidEmail_Valid $expectedResponse = [ 'errors' => [ - ['status' => 400, 'errorMessage' => 'customTextfield should be a string.'] + ['status' => 400, 'errorCode' => 'invalidCustomTextfield', 'errorMessage' => 'customTextfield should be a string.'] ], 'status' => 400 ]; diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php index de632ec2e..34017a8a9 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableAppointmentsListTest.php @@ -84,6 +84,7 @@ public function testDateMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidDate', 'errorMessage' => 'date is required and must be a valid date.', 'status' => 400, ] @@ -107,6 +108,7 @@ public function testOfficeIdMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ] @@ -130,7 +132,8 @@ public function testServiceIdMissing() $expectedResponse = [ 'errors' => [ [ - 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ] ], @@ -153,6 +156,7 @@ public function testServiceCountMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -175,10 +179,12 @@ public function testDateAndOfficeIdMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidDate', 'errorMessage' => 'date is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ] @@ -201,11 +207,13 @@ public function testDateAndServiceIdMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidDate', 'errorMessage' => 'date is required and must be a valid date.', 'status' => 400, ], [ - 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ] ], @@ -227,10 +235,12 @@ public function testDateAndServiceCountMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidDate', 'errorMessage' => 'date is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -253,11 +263,13 @@ public function testOfficeIdAndServiceIdMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ - 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ] ], @@ -279,10 +291,12 @@ public function testOfficeIdAndServiceCountMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -305,10 +319,12 @@ public function testServiceIdAndServiceCountMissing() $expectedResponse = [ 'errors' => [ [ - 'errorMessage' => 'serviceId should be a comma-separated string of integers.', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] diff --git a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php index 6364cb887..95f4defa3 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/AvailableDaysListTest.php @@ -115,6 +115,7 @@ public function testMissingStartDate() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ] @@ -139,6 +140,7 @@ public function testMissingEndDate() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ] @@ -163,6 +165,7 @@ public function testMissingOfficeId() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ] @@ -187,6 +190,7 @@ public function testMissingServiceId() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ] @@ -211,6 +215,7 @@ public function testMissingServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -236,6 +241,7 @@ public function testEmptyServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -261,6 +267,7 @@ public function testInvalidServiceCountFormat() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -280,22 +287,27 @@ public function testAllParametersMissing() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -319,10 +331,12 @@ public function testMissingStartDateAndEndDate() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ] @@ -346,10 +360,12 @@ public function testMissingOfficeIdAndServiceId() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ] @@ -373,10 +389,12 @@ public function testMissingServiceIdAndServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -400,10 +418,12 @@ public function testMissingStartDateAndOfficeId() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ] @@ -427,10 +447,12 @@ public function testMissingEndDateAndServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -454,10 +476,12 @@ public function testMissingOfficeIdAndServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -480,14 +504,17 @@ public function testMissingStartDateEndDateAndOfficeId() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ] @@ -510,14 +537,17 @@ public function testMissingStartDateEndDateAndServiceId() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceId', 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400, ] @@ -540,14 +570,17 @@ public function testMissingStartDateOfficeIdAndServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -570,14 +603,17 @@ public function testMissingEndDateOfficeIdAndServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidOfficeId', 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400, ], [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] @@ -603,10 +639,12 @@ public function testEmptyStartDateAndEndDate() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidStartDate', 'errorMessage' => 'startDate is required and must be a valid date.', 'status' => 400, ], [ + 'errorCode' => 'invalidEndDate', 'errorMessage' => 'endDate is required and must be a valid date.', 'status' => 400, ] @@ -632,6 +670,7 @@ public function testNonNumericServiceCount() $expectedResponse = [ 'errors' => [ [ + 'errorCode' => 'invalidServiceCount', 'errorMessage' => 'serviceCount should be a comma-separated string of integers.', 'status' => 400, ] diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php index aab51e2eb..7ec78de2e 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php @@ -136,7 +136,8 @@ public function testNoServiceIdProvided() 'errors' => [ [ 'offices' => [], - 'errorMessage' => 'Invalid serviceId(s).', + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400 ] ], @@ -278,8 +279,9 @@ public function testInvalidServiceId() 'errors' => [ [ 'offices' => [], - 'errorMessage' => 'Invalid service ID: blahblahblah. Must be a number.', - 'status' => 400, + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400 ] ] ]; diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php index 43ed4bd6b..70e40855e 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -141,7 +141,8 @@ public function testNoScopeIdProvided() 'errors' => [ [ 'services' => [], - 'errorMessage' => 'Invalid scopeId(s).', + 'errorCode' => 'invalidScopeId', + 'errorMessage' => "scopeId should be a 32-bit integer.", 'status' => 400 ] ], @@ -242,7 +243,8 @@ public function testInvalidScopeId() 'errors' => [ [ 'scopes' => [], - 'errorMessage' => 'Invalid scope ID: blahblahblah. Must be a number.', + 'errorCode' => 'invalidScopeId', + 'errorMessage' => 'scopeId should be a 32-bit integer.', 'status' => 400 ] ] diff --git a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php index 54305a9a8..348d7fe1d 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ServicesByOfficeListTest.php @@ -109,7 +109,8 @@ public function testNoOfficeIdProvided() 'errors' => [ [ 'services' => [], - 'errorMessage' => 'Invalid officeId(s)', + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', 'status' => 400 ] ], diff --git a/zmsentities/src/Zmsentities/Scope.php b/zmsentities/src/Zmsentities/Scope.php index 1919c6094..60d50494b 100644 --- a/zmsentities/src/Zmsentities/Scope.php +++ b/zmsentities/src/Zmsentities/Scope.php @@ -25,6 +25,11 @@ public function getSource() return $this->toProperty()->source->get(); } + public function getShortName() + { + return $this->toProperty()->shortName->get(); + } + public function getTelephoneActivated() { return $this->getPreference('client', 'telephoneActivated', null); From 005fd03f1468797b5b743eb11aa4d992f2d6ae6b Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 19:16:36 +0100 Subject: [PATCH 38/42] clean(MPDZBS-877): cleanup syntax and error handling --- zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php | 2 +- zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php | 2 +- zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php | 2 +- zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php | 4 ++-- .../src/Zmscitizenapi/AvailableAppointmentsList.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php | 2 +- zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/OfficesList.php | 4 ++-- .../src/Zmscitizenapi/OfficesServicesRelations.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/ScopesList.php | 4 ++-- .../src/Zmscitizenapi/Services/MapperService.php | 8 +++++--- .../src/Zmscitizenapi/Services/ValidationService.php | 1 + .../src/Zmscitizenapi/Services/ZmsApiClientService.php | 6 +++--- .../src/Zmscitizenapi/Services/ZmsApiFacadeService.php | 3 ++- zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/ServicesList.php | 4 ++-- 20 files changed, 39 insertions(+), 35 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php index 9b84031c8..208f26bf8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentCancel.php @@ -2,7 +2,7 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use BO\Slim\Render; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php index 4bf172834..96f56289d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentConfirm.php @@ -2,7 +2,7 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use BO\Slim\Render; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php index 05624c63a..71e29dbd1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentGet.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class AppointmentGet extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php index a7392c2f1..5477a4422 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentPreconfirm.php @@ -2,7 +2,7 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use BO\Slim\Render; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index 38e0408bd..eee60127f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -3,12 +3,12 @@ namespace BO\Zmscitizenapi; use BO\Zmscitizenapi\Application; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use BO\Zmscitizenapi\Helper\UtilityHelper; use BO\Zmscitizenapi\Services\FriendlyCaptchaService; use BO\Zmscitizenapi\Services\MapperService; use BO\Zmscitizenapi\Services\ValidationService; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; use \BO\Zmsentities\Process; use \BO\Zmsentities\Scope; use \BO\Zmsentities\Collection\ProcessList; diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index cecffabea..69d77886d 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -2,8 +2,8 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; use BO\Zmscitizenapi\Services\ValidationService; use BO\Zmscitizenapi\Helper\UtilityHelper; use BO\Zmscitizenapi\Services\ExceptionService; diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php index 6f057f8a6..544c45b8a 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableAppointmentsList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class AvailableAppointmentsList extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php index 40135c886..ef4a9014c 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php +++ b/zmscitizenapi/src/Zmscitizenapi/AvailableDaysList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class AvailableDaysList extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index 35350906c..804bddfaa 100644 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -2,7 +2,7 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use BO\Zmscitizenapi\Services\FriendlyCaptchaService; diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php index 4e2d39782..c992562ef 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; use BO\Zmscitizenapi\Services\ValidationService; class OfficesByServiceList extends BaseController diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php index 977e7606d..cf9042077 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class OfficesList extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php index 853f1cb59..71b13cf63 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesServicesRelations.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class OfficesServicesRelations extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php index 33114dfbd..535d135a0 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -2,8 +2,8 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; use BO\Zmscitizenapi\Services\ValidationService; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php index 2e5abd513..52c13cfdd 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopesList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopesList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class ScopesList extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php index 7b622d134..9defda1b8 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -41,11 +41,13 @@ public static function mapOfficesWithScope(ProviderList $providerList): array ], !empty($provider->data['address']) ? ["address" => $provider->data['address']] : [], !empty($provider->data['geo']) ? ["geo" => $provider->data['geo']] : [] - ); - + ); + $providerScope = ZmsApiFacadeService::getScopeForProvider($provider->id, $scopes); - if (isset($providerScope['scope'])) { + if (isset($providerScope['scope']) && !isset($providerScope['errors'])) { $officeData['scope'] = $providerScope['scope']; + } elseif (isset($providerScope['errors'])) { + return $providerScope['errors']; } $offices[] = $officeData; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 18b08a1d9..49abac5f4 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -5,6 +5,7 @@ use \BO\Zmsentities\Process; use \BO\Zmsentities\Collection\ProcessList; use \BO\Zmsentities\Collection\ScopeList; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class ValidationService { diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index d4781ac6d..647a059b1 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -2,9 +2,9 @@ namespace BO\Zmscitizenapi\Services; -use BO\Zmsentities\Calendar as Calendar; -use BO\Zmsentities\Process as Process; -use BO\Zmsentities\Collection\SourceList; +use \BO\Zmsentities\Calendar as Calendar; +use \BO\Zmsentities\Process as Process; +use \BO\Zmsentities\Collection\SourceList; use \BO\Zmsentities\Collection\ProcessList; use \BO\Zmsentities\Collection\ProviderList; use \BO\Zmsentities\Collection\RequestList; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index 2a7f19a41..7535c365f 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -2,7 +2,8 @@ namespace BO\Zmscitizenapi\Services; -use BO\Zmscitizenapi\Helper\UtilityHelper; +use \BO\Zmscitizenapi\Helper\UtilityHelper; +use \BO\Zmscitizenapi\Services\ZmsApiClientService; use \BO\Zmsentities\Process; use \BO\Zmsentities\Scope; use \BO\Zmsentities\Collection\ScopeList; diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php index 0bb91b0b0..31d6ea1df 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesByOfficeList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class ServicesByOfficeList extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php index 263a6e2dc..421719bd2 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ServicesList.php +++ b/zmscitizenapi/src/Zmscitizenapi/ServicesList.php @@ -2,10 +2,10 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\BaseController; +use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\ZmsApiFacadeService; +use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; class ServicesList extends BaseController { From 8de3974181581b505df5d30cb8f59abb0fb8bb2a Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 19:23:03 +0100 Subject: [PATCH 39/42] clean(MPDZBS-877): cleanup syntax and error handling --- zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php | 10 +++++----- zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php | 4 ++-- zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php | 2 +- .../src/Zmscitizenapi/OfficesByServiceList.php | 2 +- zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php | 2 +- .../Zmscitizenapi/Services/FriendlyCaptchaService.php | 2 +- zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php index eee60127f..ace057fbb 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentReserve.php @@ -2,12 +2,12 @@ namespace BO\Zmscitizenapi; -use BO\Zmscitizenapi\Application; +use \BO\Zmscitizenapi\Application; use \BO\Zmscitizenapi\BaseController; -use BO\Zmscitizenapi\Helper\UtilityHelper; -use BO\Zmscitizenapi\Services\FriendlyCaptchaService; -use BO\Zmscitizenapi\Services\MapperService; -use BO\Zmscitizenapi\Services\ValidationService; +use \BO\Zmscitizenapi\Helper\UtilityHelper; +use \BO\Zmscitizenapi\Services\FriendlyCaptchaService; +use \BO\Zmscitizenapi\Services\MapperService; +use \BO\Zmscitizenapi\Services\ValidationService; use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; use \BO\Zmsentities\Process; use \BO\Zmsentities\Scope; diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index 69d77886d..eff3022a5 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -4,8 +4,8 @@ use \BO\Zmscitizenapi\BaseController; use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; -use BO\Zmscitizenapi\Services\ValidationService; -use BO\Zmscitizenapi\Helper\UtilityHelper; +use \BO\Zmscitizenapi\Services\ValidationService; +use \BO\Zmscitizenapi\Helper\UtilityHelper; use BO\Zmscitizenapi\Services\ExceptionService; use \BO\Zmsentities\Process; use Psr\Http\Message\RequestInterface; diff --git a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php index 804bddfaa..9d5818ecb 100644 --- a/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/CaptchaGet.php @@ -5,7 +5,7 @@ use \BO\Zmscitizenapi\BaseController; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use BO\Zmscitizenapi\Services\FriendlyCaptchaService; +use \BO\Zmscitizenapi\Services\FriendlyCaptchaService; class CaptchaGet extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php index c992562ef..b030b6812 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -6,7 +6,7 @@ use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; -use BO\Zmscitizenapi\Services\ValidationService; +use \BO\Zmscitizenapi\Services\ValidationService; class OfficesByServiceList extends BaseController { diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php index 535d135a0..159b0c72b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -4,7 +4,7 @@ use \BO\Zmscitizenapi\BaseController; use \BO\Zmscitizenapi\Services\ZmsApiFacadeService; -use BO\Zmscitizenapi\Services\ValidationService; +use \BO\Zmscitizenapi\Services\ValidationService; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php b/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php index 12004612a..65435ed9b 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/FriendlyCaptchaService.php @@ -3,7 +3,7 @@ namespace BO\Zmscitizenapi\Services; use GuzzleHttp\Exception\RequestException; -use BO\Zmscitizenapi\Application; +use \BO\Zmscitizenapi\Application; use Exception; class FriendlyCaptchaService implements CaptchaServiceInterface diff --git a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php index 1a0c8060c..140dd9eca 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/CaptchaGetTest.php @@ -2,7 +2,7 @@ namespace BO\Zmscitizenapi\Tests; -use BO\Zmscitizenapi\Application; +use \BO\Zmscitizenapi\Application; class CaptchaGetTest extends Base { From 8528769a16bd1416abde832196e055679fb64a48 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 19:44:22 +0100 Subject: [PATCH 40/42] fix(MPDZBS-877): offices and services mapper --- zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php index 9defda1b8..2ee751749 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/MapperService.php @@ -46,8 +46,6 @@ public static function mapOfficesWithScope(ProviderList $providerList): array $providerScope = ZmsApiFacadeService::getScopeForProvider($provider->id, $scopes); if (isset($providerScope['scope']) && !isset($providerScope['errors'])) { $officeData['scope'] = $providerScope['scope']; - } elseif (isset($providerScope['errors'])) { - return $providerScope['errors']; } $offices[] = $officeData; From daf1593f09e4ce7e2edd6e5eeb6db83fb17c0fd8 Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Mon, 2 Dec 2024 20:06:26 +0100 Subject: [PATCH 41/42] fix(MPDZBS-877): fix reserve appointment validation --- .../Services/ValidationService.php | 1 + .../Services/ZmsApiFacadeService.php | 56 +++++++++---------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 49abac5f4..46af062ff 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -12,6 +12,7 @@ class ValidationService public static function validateServiceLocationCombination(int $officeId, array $serviceIds): array { + $availableServices = ZmsApiFacadeService::getServicesProvidedAtOffice($officeId); $availableServiceIds = array_map(function ($service) { return $service['id']; diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index 7535c365f..8801048ac 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -357,34 +357,34 @@ public static function getServicesByOfficeIds(array $officeIds): array * */ - public static function getServicesProvidedAtOffice(int $officeId): array - { - - $requestRelationList = ZmsApiClientService::getRequestRelationList(); - - $requestRelationArray = []; - foreach ($requestRelationList as $relation) { - $requestRelationArray[] = $relation; - } - - $serviceIds = array_filter($requestRelationArray, function ($relation) use ($officeId) { - return $relation->provider->id === $officeId; - }); - - $serviceIds = array_map(function ($relation) { - return $relation->request->id; - }, $serviceIds); - - $requestList = ZmsApiClientService::getServices() ?? []; - $requestArray = []; - foreach ($requestList as $request) { - $requestArray[] = $request; - } - - return array_filter($requestArray, function ($request) use ($serviceIds) { - return in_array($request->id, $serviceIds); - }); - } + public static function getServicesProvidedAtOffice(int $officeId): array + { + $requestRelationList = ZmsApiClientService::getRequestRelationList(); + + $requestRelationArray = []; + foreach ($requestRelationList as $relation) { + $requestRelationArray[] = $relation; + } + + $serviceIds = array_filter($requestRelationArray, function ($relation) use ($officeId) { + return $relation->provider->id === $officeId || (string)$relation->provider->id === (string)$officeId; + }); + + $serviceIds = array_map(function ($relation) { + return $relation->request->id; + }, $serviceIds); + + $requestList = ZmsApiClientService::getServices() ?? []; + $requestArray = []; + foreach ($requestList as $request) { + $requestArray[] = $request; + } + + return array_filter($requestArray, function ($request) use ($serviceIds) { + return in_array($request->id, $serviceIds); + }); + } + public static function getBookableFreeDays(array $queryParams): array { From f737b9eed23357cf517cff2aa192493b36597aae Mon Sep 17 00:00:00 2001 From: Thomas Fink Date: Tue, 3 Dec 2024 15:39:45 +0100 Subject: [PATCH 42/42] fix(MPDZBS-877): improve some validation for empty arrays --- .../src/Zmscitizenapi/AppointmentUpdate.php | 2 +- .../Zmscitizenapi/OfficesByServiceList.php | 2 +- .../src/Zmscitizenapi/ScopeByIdGet.php | 2 +- .../Services/ValidationService.php | 44 ++++++++++++++++--- .../Services/ZmsApiClientService.php | 2 +- .../Services/ZmsApiFacadeService.php | 3 +- .../OfficesByServiceListTest.php | 3 +- .../tests/Zmscitizenapi/ScopeByIdGetTest.php | 3 +- 8 files changed, 47 insertions(+), 14 deletions(-) diff --git a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php index eff3022a5..c779eab45 100644 --- a/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php +++ b/zmscitizenapi/src/Zmscitizenapi/AppointmentUpdate.php @@ -45,7 +45,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $updatedProcess = ZmsApiFacadeService::updateClientData(new Process($reservedProcess)); - if (isset($updatedProcess['exception']) && $updatedProcess['exception'] === 'tooManyAppointmentsWithSameMail') { + if (isset($updatedProcess['error']) && $updatedProcess['error'] === 'tooManyAppointmentsWithSameMail') { return $this->createJsonResponse($response, ExceptionService::tooManyAppointmentsWithSameMail(), 406); } diff --git a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php index b030b6812..651632432 100644 --- a/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php +++ b/zmscitizenapi/src/Zmscitizenapi/OfficesByServiceList.php @@ -22,7 +22,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $errors = ValidationService::validateServiceIdParam($serviceIdParam); if (!empty($errors)) { - return $this->createJsonResponse($response, ['errors' => $errors], 400); + return $this->createJsonResponse($response, $errors, 400); } $result = ZmsApiFacadeService::getOfficesByServiceIds($serviceIdParam); diff --git a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php index 159b0c72b..415be20a6 100644 --- a/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php +++ b/zmscitizenapi/src/Zmscitizenapi/ScopeByIdGet.php @@ -20,7 +20,7 @@ public function readResponse(RequestInterface $request, ResponseInterface $respo $errors = ValidationService::validateScopeIdParam($scopeIdParam); if (!empty($errors)) { - return $this->createJsonResponse($response, ['errors' => $errors], 400); + return $this->createJsonResponse($response, $errors, 400); } $result = ZmsApiFacadeService::getScopeByIds($scopeIdParam); diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php index 46af062ff..53e467262 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ValidationService.php @@ -199,6 +199,15 @@ public static function validateGetServicesByOfficeIds(?array $officeIds): array $errors = []; + if (empty($officeIds) || !is_array($officeIds)) { + $errors[] = [ + 'services' => [], + 'errorCode' => 'invalidOfficeId', + 'errorMessage' => 'officeId should be a 32-bit integer.', + 'status' => 400, + ]; + } + foreach ($officeIds as $id) { if (!is_numeric($id)) { $errors[] = [ @@ -253,10 +262,10 @@ public static function validateGetProcessNotFound(?Process $process): array 'status' => 404, ]; } - + return ['errors' => $errors, 'status' => 404]; } - + public static function validateScopesNotFound(?ScopeList $scopes): array { $errors = []; @@ -271,7 +280,7 @@ public static function validateScopesNotFound(?ScopeList $scopes): array return ['errors' => $errors, 'status' => 404]; } - public static function validateServicesNotFound(?array $services): array + public static function validateServicesNotFound(?array $services): array { $errors = []; if (empty($services)) { @@ -384,10 +393,20 @@ public static function validateUpdateAppointmentInputs(?int $processId, ?string return ['status' => 200, 'message' => 'Valid input for updating appointment.']; } - + public static function validateServiceIdParam(array $serviceIds): array { $errors = []; + + if (empty($serviceIds) || !is_array($serviceIds)) { + $errors[] = [ + 'offices' => [], + 'errorCode' => 'invalidServiceId', + 'errorMessage' => 'serviceId should be a 32-bit integer.', + 'status' => 400, + ]; + return ['errors' => $errors, 'status' => 400]; + } foreach ($serviceIds as $id) { if (!is_numeric($id)) { @@ -400,12 +419,23 @@ public static function validateServiceIdParam(array $serviceIds): array } } - return empty($errors) ? [] : $errors; - } + return empty($errors) ? [] : ['errors' => $errors, 'status' => 400]; + } public static function validateScopeIdParam(array $scopeIds): array { $errors = []; + + if (empty($scopeIds) || !is_array($scopeIds)) { + $errors[] = [ + 'scopes' => [], + 'errorCode' => 'invalidScopeId', + 'errorMessage' => 'scopeId should be a 32-bit integer.', + 'status' => 400, + ]; + return ['errors' => $errors, 'status' => 400]; + } + foreach ($scopeIds as $id) { if (!is_numeric($id)) { $errors[] = [ @@ -417,7 +447,7 @@ public static function validateScopeIdParam(array $scopeIds): array } } - return empty($errors) ? [] : $errors; + return empty($errors) ? [] : ['errors' => $errors, 'status' => 400]; } } diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php index 647a059b1..9bbfc5306 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiClientService.php @@ -166,7 +166,7 @@ public static function submitClientData(Process $process): Process|array $exceptionName = json_decode(json_encode($e), true)['template'] ?? null; if ($exceptionName === 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail') { $exception = [ - 'exception' => 'tooManyAppointmentsWithSameMail' + 'error' => 'tooManyAppointmentsWithSameMail' ]; return $exception; } else { diff --git a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php index 8801048ac..c2d53237c 100644 --- a/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php +++ b/zmscitizenapi/src/Zmscitizenapi/Services/ZmsApiFacadeService.php @@ -438,7 +438,7 @@ public static function getBookableFreeDays(array $queryParams): array } } - public static function getFreeAppointments(array $params): mixed + public static function getFreeAppointments(array $params): ProcessList|array { $office = [ 'id' => $params['officeId'], @@ -458,6 +458,7 @@ public static function getFreeAppointments(array $params): mixed try { + $freeSlots = new ProcessList(); $freeSlots = ZmsApiClientService::getFreeTimeslots( new ProviderList([$office]), new RequestList($requests), diff --git a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php index 7ec78de2e..b889e59da 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/OfficesByServiceListTest.php @@ -283,7 +283,8 @@ public function testInvalidServiceId() 'errorMessage' => 'serviceId should be a 32-bit integer.', 'status' => 400 ] - ] + ], + "status" => 400 ]; $this->assertEquals(400, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true)); diff --git a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php index 70e40855e..10557ec2e 100644 --- a/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php +++ b/zmscitizenapi/tests/Zmscitizenapi/ScopeByIdGetTest.php @@ -247,7 +247,8 @@ public function testInvalidScopeId() 'errorMessage' => 'scopeId should be a 32-bit integer.', 'status' => 400 ] - ] + ], + "status" => 400 ]; $this->assertEquals(400, $response->getStatusCode()); $this->assertEqualsCanonicalizing($expectedResponse, json_decode((string)$response->getBody(), true));