Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Web/Services/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,13 @@ function RegisterResources(SlimServer $server, SlimWebServiceRegistry $registry)
{
$resourceRepository = new ResourceRepository();
$attributeService = new AttributeService(new AttributeRepository());
$webService = new ResourcesWebService($server, $resourceRepository, $attributeService, new ReservationViewRepository());
$webService = new ResourcesWebService(
server: $server,
resourceRepository: $resourceRepository,
attributeService: $attributeService,
reservationRepository: new ReservationViewRepository(),
scheduleRepository: new ScheduleRepository()
);
$writeWebService = new ResourcesWriteWebService($server, new ResourceSaveController($resourceRepository, new ResourceRequestValidator($attributeService)));

$roGroupId = GetConfigGroup(ConfigKeys::API_RESOURCES_RO_GROUP);
Expand Down
111 changes: 73 additions & 38 deletions WebServices/ResourcesWebService.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,61 +16,45 @@

class ResourcesWebService
{
/**
* @var IRestServer
*/
private $server;

/**
* @var IResourceRepository
*/
private $resourceRepository;

/**
* @var IAttributeService
*/
private $attributeService;

/**
* @var IReservationViewRepository
*/
private $reservationRepository;

public function __construct(
IRestServer $server,
IResourceRepository $resourceRepository,
IAttributeService $attributeService,
IReservationViewRepository $reservationRepository
private IRestServer $server,
private IResourceRepository $resourceRepository,
private IAttributeService $attributeService,
private IReservationViewRepository $reservationRepository,
private IScheduleRepository $scheduleRepository
) {
$this->server = $server;
$this->resourceRepository = $resourceRepository;
$this->attributeService = $attributeService;
$this->reservationRepository = $reservationRepository;
}

/**
* @name GetAllResources
* @description Loads all resources
* Optional query string parameter: scheduleId. One or more schedule IDs, comma-separated (e.g. scheduleId=1,2,3). If provided, only resources belonging to those schedules will be returned. Each value must be a positive integer (greater than zero); if any value is non-integer or zero, a 400 Bad Request is returned.
* Optional query string parameter: scheduleId. One or more schedule IDs, comma-separated (e.g. scheduleId=1,2,3). If provided, only resources belonging to those schedules will be returned. Each value must be a positive integer (greater than zero); if any value is non-integer or zero, a 400 Bad Request is returned. If any schedule ID does not exist, a 404 Not Found is returned.
* Optional query string parameter: groupId. One or more resource group IDs, comma-separated (e.g. groupId=1,2,3). If provided, only resources belonging to those groups (including sub-groups) will be returned. Each value must be a positive integer (greater than zero); if any value is non-integer or zero, a 400 Bad Request is returned. If any group ID does not exist, a 404 Not Found is returned.
* @response ResourcesResponse
* @return void
*/
public function GetAll()
{
$scheduleIds = null;
$scheduleIdParam = $this->server->GetQueryString(WebServiceQueryStringKeys::SCHEDULE_ID);
if ($scheduleIdParam !== null && $scheduleIdParam !== '') {
$rawIds = explode(',', $scheduleIdParam);
foreach ($rawIds as $id) {
if (!ctype_digit($id) || (int)$id === 0) {
$scheduleIds = $this->parsePositiveIntegerIds(paramName: WebServiceQueryStringKeys::SCHEDULE_ID);
if ($scheduleIds === false) {
return;
}

$groupIds = $this->parsePositiveIntegerIds(paramName: WebServiceQueryStringKeys::GROUP_ID);
if ($groupIds === false) {
return;
}

if ($scheduleIds !== null) {
foreach ($scheduleIds as $scheduleId) {
if ($this->scheduleRepository->LoadById($scheduleId) === null) {
$this->server->WriteResponse(
RestResponse::BadRequest("Invalid scheduleId '$id': must be a positive integer"),
RestResponse::BAD_REQUEST_CODE
restResponse: RestResponse::NotFound(),
statusCode: RestResponse::NOT_FOUND_CODE
);
return;
}
}
$scheduleIds = array_map('intval', $rawIds);
}

$resources = $this->resourceRepository->GetUserResourceList();
Expand All @@ -85,6 +69,29 @@ public function GetAll()
$resources = $filteredResources;
}

if ($groupIds !== null) {
// GetResourceGroups() returns the full tree, used for both existence checks and sub-group traversal.
$groupTree = $this->resourceRepository->GetResourceGroups();
$groupList = $groupTree->GetGroupList();
$groupResourceIds = [];
foreach ($groupIds as $groupId) {
if (!array_key_exists($groupId, $groupList)) {
$this->server->WriteResponse(RestResponse::NotFound(), RestResponse::NOT_FOUND_CODE);
return;
}
// GetResourceIds() recurses into sub-groups.
$groupResourceIds = array_merge($groupResourceIds, $groupTree->GetResourceIds($groupId));
}
$groupResourceIds = array_unique($groupResourceIds);
$filteredResources = [];
foreach ($resources as $resource) {
if (in_array($resource->GetId(), $groupResourceIds)) {
$filteredResources[] = $resource;
}
}
$resources = $filteredResources;
}

$resourceIds = [];
foreach ($resources as $resource) {
$resourceIds[] = $resource->GetId();
Expand Down Expand Up @@ -224,6 +231,34 @@ public function GetGroups()
$this->server->WriteResponse(new ResourceGroupsResponse($groups));
}

/**
* Parses a comma-separated list of positive integers from a query string parameter.
* Duplicate values are removed; the returned array is unique and re-indexed.
*
* @return int[]|false|null int[] on success, null if param absent/empty, false if invalid
* (a 400 response has already been written)
*/
private function parsePositiveIntegerIds(string $paramName): array|false|null
{
$param = $this->server->GetQueryString($paramName);
if ($param === null || $param === '') {
return null;
}

$rawIds = explode(',', $param);
foreach ($rawIds as $id) {
if (!ctype_digit($id) || (int)$id === 0) {
$this->server->WriteResponse(
RestResponse::BadRequest("Invalid $paramName '$id': must be a positive integer"),
RestResponse::BAD_REQUEST_CODE
);
return false;
}
}

return array_values(array_unique(array_map('intval', $rawIds)));
}

/**
* @param BookableResource $resource
* @param ReservationItemView[][] $reservations
Expand Down
16 changes: 15 additions & 1 deletion docs/source/API.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ To combine multiple parameters::

/Web/Services/index.php/Reservations/?scheduleId=1&userId=5

To filter resources by group::

/Web/Services/index.php/Resources/?groupId=1

/Web/Services/index.php/Resources/?groupId=1,2,3

POST Requests
~~~~~~~~~~~~~

Expand Down Expand Up @@ -2165,7 +2171,15 @@ Optional query string parameter: ``scheduleId``. One or more schedule IDs,
comma-separated (e.g. ``scheduleId=1,2,3``). If provided, only resources
belonging to those schedules will be returned. Each value must be a positive
integer (greater than zero); if any value is non-integer or zero, a ``400 Bad
Request`` is returned.
Request`` is returned. If any schedule ID does not exist, a ``404 Not Found``
is returned.

Optional query string parameter: ``groupId``. One or more resource group IDs,
comma-separated (e.g. ``groupId=1,2,3``). If provided, only resources belonging
to those groups (including resources in sub-groups) will be returned. Each value
must be a positive integer (greater than zero); if any value is non-integer or
zero, a ``400 Bad Request`` is returned. If any group ID does not exist, a
``404 Not Found`` is returned.

**Route:** ``/Web/Services/index.php/Resources/``

Expand Down
1 change: 1 addition & 0 deletions lib/WebService/WebServiceQueryStringKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class WebServiceQueryStringKeys
public const DATE_TIME = 'dateTime';
public const START_DATE_TIME = 'startDateTime';
public const END_DATE_TIME = 'endDateTime';
public const GROUP_ID = 'groupId';
public const RESOURCE_ID = 'resourceId';
public const SCHEDULE_ID = 'scheduleId';
public const UPDATE_SCOPE = 'updateScope';
Expand Down
Loading