Appointments Slot Support
Signed-off-by: Anna Larch <anna@nextcloud.com>
This commit is contained in:
parent
84d70110c5
commit
9087968333
|
@ -15,7 +15,7 @@
|
|||
* ☑️ Tasks! See tasks with a due date directly in the calendar
|
||||
* 🙈 **We’re not reinventing the wheel!** Based on the great [c-dav library](https://github.com/nextcloud/cdav-library), [ical.js](https://github.com/mozilla-comm/ical.js) and [fullcalendar](https://github.com/fullcalendar/fullcalendar) libraries.
|
||||
]]></description>
|
||||
<version>2.4.0-RC.2</version>
|
||||
<version>2.4.0-RC.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author homepage="https://georg.coffee">Georg Ehrke</author>
|
||||
<author homepage="https://tcit.fr">Thomas Citharel</author>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,230 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Controller;
|
||||
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Exception\ServiceException;
|
||||
use OCA\Calendar\Http\JsonResponse;
|
||||
use OCA\Calendar\Service\Appointments\AppointmentConfigService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IInitialStateService;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUser;
|
||||
|
||||
/**
|
||||
* Class PublicViewController
|
||||
*
|
||||
* @package OCA\Calendar\Controller
|
||||
*/
|
||||
class AppointmentConfigController extends Controller {
|
||||
|
||||
/** @var IInitialStateService */
|
||||
private $initialStateService;
|
||||
|
||||
/** @var IUser */
|
||||
private $user;
|
||||
|
||||
/** @var AppointmentConfigService */
|
||||
private $appointmentConfigService;
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
* @param IRequest $request an instance of the request
|
||||
* @param IInitialStateService $initialStateService
|
||||
* @param IUser $user
|
||||
* @param AppointmentConfigService $appointmentService
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
IInitialStateService $initialStateService,
|
||||
IUser $user,
|
||||
AppointmentConfigService $appointmentService) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->initialStateService = $initialStateService;
|
||||
$this->user = $user;
|
||||
$this->appointmentConfigService = $appointmentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $renderAs
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function index(string $renderAs
|
||||
):TemplateResponse {
|
||||
$appointmentConfigs = [];
|
||||
try {
|
||||
$appointmentConfigs = $this->appointmentConfigService->getAllAppointmentConfigurations($this->user->getUID());
|
||||
} catch (ServiceException $e) {
|
||||
// do nothing and don't show any appointments
|
||||
}
|
||||
|
||||
$this->initialStateService->provideInitialState($this->appName, 'appointmentConfigurations', $appointmentConfigs);
|
||||
|
||||
return new TemplateResponse($this->appName, 'main', [
|
||||
], $renderAs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @param string $location
|
||||
* @param string $visibility
|
||||
* @param string $targetCalendarUri
|
||||
* @param string $availability
|
||||
* @param int $length
|
||||
* @param int $increment
|
||||
* @param int $preparationDuration
|
||||
* @param int $followupDuration
|
||||
* @param int $buffer
|
||||
* @param int|null $dailyMax
|
||||
* @param string|null $freebusyUris
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function create(
|
||||
string $name,
|
||||
string $description,
|
||||
string $location,
|
||||
string $visibility,
|
||||
string $targetCalendarUri,
|
||||
string $availability,
|
||||
int $length,
|
||||
int $increment,
|
||||
int $preparationDuration = 0,
|
||||
int $followupDuration = 0,
|
||||
int $buffer = 0,
|
||||
?int $dailyMax = null,
|
||||
?string $freebusyUris = null): JsonResponse {
|
||||
$appointmentConfig = new AppointmentConfig();
|
||||
$appointmentConfig->setName($name);
|
||||
$appointmentConfig->setDescription($description);
|
||||
$appointmentConfig->setLocation($location);
|
||||
$appointmentConfig->setVisibility($visibility);
|
||||
$appointmentConfig->setUserId($this->user->getUID());
|
||||
$appointmentConfig->setTargetCalendarUri($targetCalendarUri);
|
||||
$appointmentConfig->setAvailability($availability);
|
||||
$appointmentConfig->setLength($length);
|
||||
$appointmentConfig->setIncrement($increment);
|
||||
$appointmentConfig->setPreparationDuration($preparationDuration);
|
||||
$appointmentConfig->setFollowupDuration($followupDuration);
|
||||
$appointmentConfig->setBuffer($buffer);
|
||||
$appointmentConfig->setDailyMax($dailyMax);
|
||||
$appointmentConfig->setCalendarFreebusyUris($freebusyUris);
|
||||
|
||||
try {
|
||||
$appointmentConfig = $this->appointmentConfigService->create($appointmentConfig);
|
||||
return JsonResponse::success($appointmentConfig);
|
||||
} catch (ServiceException $e) {
|
||||
return JsonResponse::errorFromThrowable($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function show(int $id): JsonResponse {
|
||||
try {
|
||||
$appointmentConfig = $this->appointmentConfigService->findByIdAndUser($id, $this->user->getUID());
|
||||
return JsonResponse::success($appointmentConfig);
|
||||
} catch (ServiceException $e) {
|
||||
return JsonResponse::errorFromThrowable($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @param string $location
|
||||
* @param string $visibility
|
||||
* @param string $targetCalendarUri
|
||||
* @param string $availability
|
||||
* @param int $length
|
||||
* @param int $increment
|
||||
* @param int $preparationDuration
|
||||
* @param int $followupDuration
|
||||
* @param int $buffer
|
||||
* @param int|null $dailyMax
|
||||
* @param string|null $freebusyUris
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(
|
||||
int $id,
|
||||
string $name,
|
||||
string $description,
|
||||
string $location,
|
||||
string $visibility,
|
||||
string $targetCalendarUri,
|
||||
string $availability,
|
||||
int $length,
|
||||
int $increment,
|
||||
int $preparationDuration = 0,
|
||||
int $followupDuration = 0,
|
||||
int $buffer = 0,
|
||||
?int $dailyMax = null,
|
||||
?string $freebusyUris = null): JsonResponse {
|
||||
try {
|
||||
$appointmentConfig = $this->appointmentConfigService->findByIdAndUser($id, $this->user->getUID());
|
||||
} catch (ServiceException $e) {
|
||||
return JsonResponse::errorFromThrowable($e);
|
||||
}
|
||||
|
||||
$appointmentConfig->setName($name);
|
||||
$appointmentConfig->setDescription($description);
|
||||
$appointmentConfig->setLocation($location);
|
||||
$appointmentConfig->setVisibility($visibility);
|
||||
$appointmentConfig->setUserId($this->user->getUID());
|
||||
$appointmentConfig->setTargetCalendarUri($targetCalendarUri);
|
||||
$appointmentConfig->setAvailability($availability);
|
||||
$appointmentConfig->setLength($length);
|
||||
$appointmentConfig->setIncrement($increment);
|
||||
$appointmentConfig->setPreparationDuration($preparationDuration);
|
||||
$appointmentConfig->setFollowupDuration($followupDuration);
|
||||
$appointmentConfig->setBuffer($buffer);
|
||||
$appointmentConfig->setDailyMax($dailyMax);
|
||||
$appointmentConfig->setCalendarFreebusyUris($freebusyUris);
|
||||
|
||||
try {
|
||||
$appointmentConfig = $this->appointmentConfigService->update($appointmentConfig);
|
||||
return JsonResponse::success($appointmentConfig);
|
||||
} catch (ServiceException $e) {
|
||||
return JsonResponse::errorFromThrowable($e, 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(int $id): JsonResponse {
|
||||
try {
|
||||
$this->appointmentConfigService->delete($id, $this->user->getUID());
|
||||
return JsonResponse::success();
|
||||
} catch (ServiceException $e) {
|
||||
return JsonResponse::errorFromThrowable($e, 403);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Controller;
|
||||
|
||||
use OCA\Calendar\Exception\ServiceException;
|
||||
use OCA\Calendar\Http\JsonResponse;
|
||||
use OCA\Calendar\Service\Appointments\AppointmentConfigService;
|
||||
use OCA\Calendar\Service\Appointments\Booking;
|
||||
use OCA\Calendar\Service\Appointments\BookingService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Calendar\IManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\IInitialStateService;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
/**
|
||||
* Class PublicViewController
|
||||
*
|
||||
* @package OCA\Calendar\Controller
|
||||
*/
|
||||
class BookingController extends Controller {
|
||||
|
||||
/** @var BookingService */
|
||||
private $bookingService;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
/** @var AppointmentConfigService */
|
||||
private $appointmentConfigService;
|
||||
|
||||
/** @var IManager */
|
||||
private $manager;
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
* @param IRequest $request an instance of the request
|
||||
* @param IConfig $config
|
||||
* @param IInitialStateService $initialStateService
|
||||
* @param IURLGenerator $urlGenerator
|
||||
*/
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
ITimeFactory $timeFactory,
|
||||
BookingService $bookingService,
|
||||
AppointmentConfigService $appointmentConfigService,
|
||||
IManager $manager) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->bookingService = $bookingService;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->appointmentConfigService = $appointmentConfigService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ServiceException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function getBookableSlots(int $appointmentConfigId, int $unixStartTime, int $unixEndTime) {
|
||||
// rate limit this to only allow ranges between 0 to 7 days
|
||||
if (ceil(($unixEndTime - $unixStartTime) / 86400) > 7) {
|
||||
return JsonResponse::error('Date Range too large.', 403);
|
||||
}
|
||||
|
||||
if ($this->timeFactory->getTime() > $unixStartTime || $this->timeFactory->getTime() > $unixEndTime) {
|
||||
throw new ServiceException('Booking time must be in the future', 403);
|
||||
}
|
||||
|
||||
$appointmentConfig = $this->appointmentConfigService->findById($appointmentConfigId);
|
||||
$booking = new Booking($appointmentConfig, $unixStartTime, $unixEndTime);
|
||||
$data = $this->bookingService->getSlots($booking);
|
||||
return JsonResponse::success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $calendarData
|
||||
*/
|
||||
public function bookSlot(string $calendarData) {
|
||||
$this->bookingService->book($calendarData);
|
||||
return JsonResponse::success();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
// @TODO rename and adjust types from migration
|
||||
|
||||
namespace OCA\Calendar\Db;
|
||||
|
||||
use JsonSerializable;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
|
||||
/**
|
||||
* @method int getId()
|
||||
* @method void setId(int $id)
|
||||
* @method string getToken()
|
||||
* @method void setToken(string $token)
|
||||
* @method string getName()
|
||||
* @method void setName(string $name)
|
||||
* @method string getDescription()
|
||||
* @method void setDescription(string $name)
|
||||
* @method string getLocation()
|
||||
* @method void setLocation(?string $name)
|
||||
* @method string getVisibility()
|
||||
* @method void setVisibility(string $visibility)
|
||||
* @method string getUserId()
|
||||
* @method void setUserId(string $userId)
|
||||
* @method string getTargetCalendarUri()
|
||||
* @method void setTargetCalendarUri(string $calendarUri)
|
||||
* @method string|null getCalendarFreebusyUris()
|
||||
* @method void setCalendarFreebusyUris(?string $freebusyUris)
|
||||
* @method string getAvailability()
|
||||
* @method void setAvailability(string $availability)
|
||||
* @method int getLength()
|
||||
* @method void setLength(int $length)
|
||||
* @method int getIncrement()
|
||||
* @method void setIncrement(int $increment)
|
||||
* @method int getPreparationDuration()
|
||||
* @method void setPreparationDuration(int $prepDuration)
|
||||
* @method int getFollowupDuration()
|
||||
* @method void setFollowupDuration(int $followup)
|
||||
* @method int getBuffer()
|
||||
* @method void setBuffer(int $buffer)
|
||||
* @method int|null getDailyMax()
|
||||
* @method void setDailyMax(?int $max)
|
||||
*/
|
||||
class AppointmentConfig extends Entity implements JsonSerializable {
|
||||
|
||||
/** @var string */
|
||||
protected $token;
|
||||
|
||||
/** @var string */
|
||||
protected $name = '';
|
||||
|
||||
/** @var string|null */
|
||||
protected $description;
|
||||
|
||||
/** @var string|null */
|
||||
protected $location;
|
||||
|
||||
/** @var string */
|
||||
protected $visibility;
|
||||
|
||||
/** @var string */
|
||||
protected $userId;
|
||||
|
||||
/** @var string */
|
||||
protected $targetCalendarUri;
|
||||
|
||||
/** @var string|null */
|
||||
protected $calendarFreebusyUris;
|
||||
|
||||
/** @var string */
|
||||
protected $availability;
|
||||
|
||||
/** @var int */
|
||||
protected $length;
|
||||
|
||||
/** @var int */
|
||||
protected $increment;
|
||||
|
||||
/** @var int */
|
||||
protected $preparationDuration;
|
||||
|
||||
/** @var int */
|
||||
protected $followupDuration;
|
||||
|
||||
/** @var int */
|
||||
protected $buffer;
|
||||
|
||||
/** @var int|null */
|
||||
protected $dailyMax;
|
||||
|
||||
/** @var string */
|
||||
public const VISIBILITY_PUBLIC = 'PUBLIC';
|
||||
|
||||
/** @var string */
|
||||
public const VISIBILITY_PRIVATE = 'PRIVATE';
|
||||
|
||||
/**
|
||||
* Total length of one slot of the appointment config
|
||||
* in minutes
|
||||
*
|
||||
* @return int Minutes of Appointment slot length
|
||||
*/
|
||||
public function getTotalLength(): int {
|
||||
return $this->getLength() + $this->getPreparationDuration() + $this->getFollowupDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Principals always have the same format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrincipalUri() : string {
|
||||
return 'principals/users/' . $this->userId;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'token' => $this->getToken(),
|
||||
'name' => $this->getName(),
|
||||
'description' => $this->getDescription(),
|
||||
'location' => $this->getLocation(),
|
||||
'visibility' => $this->getVisibility(),
|
||||
'userId' => $this->getUserId(),
|
||||
'calendarUri' => $this->getTargetCalendarUri(),
|
||||
'calendarFreeBusyUris' => $this->getCalendarFreebusyUris(),
|
||||
'availability' => $this->getAvailability(),
|
||||
'length' => $this->getLength(),
|
||||
'increment' => $this->getIncrement(),
|
||||
'preparationDuration' => $this->getPreparationDuration(),
|
||||
'followUpDuration' => $this->getFollowupDuration(),
|
||||
'totalLength' => $this->getTotalLength(),
|
||||
'buffer' => $this->getBuffer(),
|
||||
'dailyMax' => $this->getDailyMax()
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Calendar\Db;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\Exception as DbException;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @template-extends QBMapper<AppointmentConfig>
|
||||
*/
|
||||
class AppointmentConfigMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'calendar_appt_configs');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $userId
|
||||
* @return AppointmentConfig
|
||||
* @throws DbException
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function findByIdForUser(int $id, string $userId) : AppointmentConfig {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT))
|
||||
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR));
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return AppointmentConfig
|
||||
* @throws DbException
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function findById(int $id) : AppointmentConfig {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT));
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return AppointmentConfig
|
||||
* @throws DbException
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function findByToken(string $token) : AppointmentConfig {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('token', $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR));
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $userId
|
||||
* @return AppointmentConfig[]
|
||||
* @throws DbException
|
||||
*/
|
||||
public function findAllForUser(string $userId): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR));
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $userId
|
||||
* @return int
|
||||
* @throws DbException
|
||||
*/
|
||||
public function deleteById(int $id, string $userId): int {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->delete($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT))
|
||||
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR));
|
||||
|
||||
return $qb->executeStatement();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* Calendar
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Calendar\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ServiceException extends Exception {
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author 2021 Anna Larch
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Calendar\Http;
|
||||
|
||||
use JsonSerializable;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse as Base;
|
||||
use Throwable;
|
||||
use function array_flip;
|
||||
use function array_intersect_key;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
* @see https://github.com/omniti-labs/jsend
|
||||
*/
|
||||
class JsonResponse extends Base {
|
||||
public function __construct($data = [],
|
||||
int $statusCode = Http::STATUS_OK) {
|
||||
parent::__construct($data, $statusCode);
|
||||
|
||||
$this->addHeader('x-calendar-response', 'true');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|JsonSerializable|bool|string $data
|
||||
* @param int $status
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function success($data = null,
|
||||
int $status = Http::STATUS_OK): self {
|
||||
return new self(
|
||||
[
|
||||
'status' => 'success',
|
||||
'data' => $data,
|
||||
],
|
||||
$status
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|JsonSerializable|bool|string $data
|
||||
* @param int $status
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function fail($data = null,
|
||||
int $status = Http::STATUS_BAD_REQUEST): self {
|
||||
return new self(
|
||||
[
|
||||
'status' => 'fail',
|
||||
'data' => $data,
|
||||
],
|
||||
$status
|
||||
);
|
||||
}
|
||||
|
||||
public static function error(string $message,
|
||||
int $status = Http::STATUS_INTERNAL_SERVER_ERROR,
|
||||
array $data = [],
|
||||
int $code = 0): self {
|
||||
return new self(
|
||||
[
|
||||
'status' => 'error',
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
'code' => $code,
|
||||
],
|
||||
$status
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
*/
|
||||
public static function errorFromThrowable(Throwable $error,
|
||||
int $status = Http::STATUS_INTERNAL_SERVER_ERROR,
|
||||
array $data = []): self {
|
||||
return self::error(
|
||||
$error->getMessage(),
|
||||
$status,
|
||||
array_merge(
|
||||
$data,
|
||||
self::serializeException($error)
|
||||
),
|
||||
$error->getCode()
|
||||
);
|
||||
}
|
||||
|
||||
private static function serializeException(?Throwable $throwable): ?array {
|
||||
if ($throwable === null) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'type' => get_class($throwable),
|
||||
'message' => $throwable->getMessage(),
|
||||
'code' => $throwable->getCode(),
|
||||
'trace' => self::filterTrace($throwable->getTrace()),
|
||||
'previous' => self::serializeException($throwable->getPrevious()),
|
||||
];
|
||||
}
|
||||
|
||||
private static function filterTrace(array $original): array {
|
||||
return array_map(function (array $row) {
|
||||
return array_intersect_key($row,
|
||||
array_flip(['file', 'line', 'function', 'class']));
|
||||
}, $original);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Calendar\Migration;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\DB\Types;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version2040Date20210908101001 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$table = $schema->createTable('calendar_appt_configs');
|
||||
$table->addColumn('id', Types::BIGINT, [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
'unsigned' => true
|
||||
]);
|
||||
// Appointment
|
||||
$table->addColumn('token', Types::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 128
|
||||
]);
|
||||
// Appointment
|
||||
$table->addColumn('name', Types::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 128
|
||||
]);
|
||||
$table->addColumn('description', Types::TEXT, [
|
||||
'notnull' => false,
|
||||
'length' => null
|
||||
]);
|
||||
$table->addColumn('location', Types::TEXT, [
|
||||
'notnull' => false,
|
||||
'length' => null
|
||||
]);
|
||||
//Visibility [enum] - PUBLIC (shown somewhere on the user's profile), PRIVATE (only shareable by link) - possibly other variations?
|
||||
$table->addColumn('visibility', Types::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 7
|
||||
]);
|
||||
$table->addColumn('user_id', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 64
|
||||
]);
|
||||
$table->addColumn('target_calendar_uri', Types::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255
|
||||
]);
|
||||
//Calendar(s) for conflict handling [string array]
|
||||
$table->addColumn('calendar_freebusy_uris', Types::TEXT, [
|
||||
'notnull' => false,
|
||||
'length' => null
|
||||
]);
|
||||
//Slot availabilities [RRULE] - false for notnull bc db doesn't allow default values for blob types
|
||||
$table->addColumn('availability', Types::TEXT, [
|
||||
'notnull' => false,
|
||||
'length' => null,
|
||||
]);
|
||||
$table->addColumn('length', Types::INTEGER, [
|
||||
'notnull' => true
|
||||
]);
|
||||
$table->addColumn('increment', Types::INTEGER, [
|
||||
'notnull' => true
|
||||
]);
|
||||
$table->addColumn('preparation_duration', Types::INTEGER, [
|
||||
'notnull' => true,
|
||||
'default' => 0
|
||||
]);
|
||||
$table->addColumn('followup_duration', Types::INTEGER, [
|
||||
'notnull' => true,
|
||||
'default' => 0
|
||||
]);
|
||||
$table->addColumn('buffer', Types::INTEGER, [
|
||||
'notnull' => true,
|
||||
'default' => 0
|
||||
]);
|
||||
//Maximum slots per day - if 0, fit as many as possible
|
||||
$table->addColumn('daily_max', Types::INTEGER, [
|
||||
'notnull' => false,
|
||||
'default' => null
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Db\AppointmentConfigMapper;
|
||||
use OCA\Calendar\Exception\ServiceException;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\DB\Exception as DbException;
|
||||
|
||||
class AppointmentConfigService {
|
||||
|
||||
/** @var AppointmentConfigMapper */
|
||||
private $mapper;
|
||||
|
||||
/**
|
||||
* @param AppointmentConfigMapper $mapper
|
||||
*/
|
||||
public function __construct(AppointmentConfigMapper $mapper) {
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @return AppointmentConfig[]
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getAllAppointmentConfigurations(string $user): array {
|
||||
try {
|
||||
return $this->mapper->findAllForUser($user);
|
||||
} catch (DbException $e) {
|
||||
throw new ServiceException('Error fetching configs', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $userId
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function delete(int $id, string $userId): void {
|
||||
try {
|
||||
$this->mapper->deleteById($id, $userId);
|
||||
} catch (DbException $e) {
|
||||
throw new ServiceException('Could not delete appointment', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AppointmentConfig $appointmentConfig
|
||||
* @return AppointmentConfig
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function update(AppointmentConfig $appointmentConfig): AppointmentConfig {
|
||||
try {
|
||||
return $this->mapper->update($appointmentConfig);
|
||||
} catch (DbException $e) {
|
||||
throw new ServiceException('Could not update Appointment', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return AppointmentConfig
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function findById(int $id): AppointmentConfig {
|
||||
try {
|
||||
return $this->mapper->findById($id);
|
||||
} catch (DbException |DoesNotExistException|MultipleObjectsReturnedException $e) {
|
||||
throw new ServiceException('Could not find a record for id', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return AppointmentConfig
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function findByIdAndUser(int $id, string $userId): AppointmentConfig {
|
||||
try {
|
||||
return $this->mapper->findByIdForUser($id, $userId);
|
||||
} catch (DbException |DoesNotExistException|MultipleObjectsReturnedException $e) {
|
||||
throw new ServiceException('Could not find a record for id', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AppointmentConfig $appointmentConfig
|
||||
* @return AppointmentConfig
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function create(AppointmentConfig $appointmentConfig): AppointmentConfig {
|
||||
try {
|
||||
return $this->mapper->insert($appointmentConfig);
|
||||
} catch (DbException $e) {
|
||||
throw new ServiceException('Could not create new appointment', $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id ?? maybe pass the appointment
|
||||
* @param int $unixStartTime
|
||||
* @param int $unixEndTime
|
||||
* @return array
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public function getSlots(AppointmentConfig $appointmentConfig, int $unixStartTime, int $unixEndTime, string $outboxUri): array {
|
||||
// rate limit this to only allow ranges between 0 to 7 days?
|
||||
|
||||
// move this to controller
|
||||
//ITimeFactory
|
||||
if (time() > $unixStartTime || time() > $unixEndTime) {
|
||||
throw new ServiceException('Booking time must be in the future', 403);
|
||||
}
|
||||
|
||||
// move this to controller
|
||||
try {
|
||||
$appointment = $this->mapper->findByIdForUser($id);
|
||||
} catch (DbException |DoesNotExistException|MultipleObjectsReturnedException $e) {
|
||||
throw new ServiceException('Appointment not found', 404, $e);
|
||||
}
|
||||
|
||||
//do i need to check the recurrence rule here?
|
||||
$availability = $this->parseRRule($appointment->getAvailability());
|
||||
|
||||
// move this to create
|
||||
$totalLength = $appointment->getTotalLength() * 60;
|
||||
// if($totalLength === 0){
|
||||
// throw new ServiceException('Appointment not bookable');
|
||||
// }
|
||||
|
||||
$bookedSlots = $this->findBookedSlotsAmount($id, $unixStartTime, $unixEndTime);
|
||||
|
||||
// negotiate avaliable slots
|
||||
$bookableSlots = ($appointment->getDailyMax() !== null) ? $appointment->getDailyMax() - $bookedSlots : 99999;
|
||||
|
||||
if ($bookableSlots <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$slots = [];
|
||||
$timeblocks = $this->getCalendarFreeTimeblocks($unixStartTime, $unixEndTime, $outboxUri, $appointment->getCalendarFreebusyUris());
|
||||
// get slots irrespective of conflicts
|
||||
// Remove unavailable slots via comparing with RRule
|
||||
// remove conflicting slots via calendar free busy
|
||||
// check daily max via bookable slots via TDO Slot object
|
||||
|
||||
|
||||
// @TODO - refactor this to functions
|
||||
// foreach($timeblocks as $calendarTimeblock){
|
||||
// $time = $calendarTimeblock['start'];
|
||||
// // we only render slots that fit into the time frame given
|
||||
// while(($time + $totalLength) <= $calendarTimeblock['end'] ) {
|
||||
// $slots[] = ['start' => $time];
|
||||
// // add the increment
|
||||
// $time += $appointment->getIncrement()*60;
|
||||
// // reduce the amount of available slots
|
||||
// $bookableSlots--;
|
||||
// if($bookableSlots <= 0) {
|
||||
// // no more slots, let's break the outer loop, too
|
||||
// break 2;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return $slots; // make a DTO out of this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $time
|
||||
* @param string $outboxUri
|
||||
* @param array $freeBusyUris
|
||||
* @return [][]
|
||||
*
|
||||
* Check if slot is conflicting with existing appointments
|
||||
* returns the start and end times of free blocks
|
||||
* [
|
||||
* ['start' => 12345678, 'end' => 1234567]
|
||||
* ['start' => 12345678, 'end' => 1234567]
|
||||
* ]
|
||||
*/
|
||||
public function getCalendarFreeTimeblocks(int $startTime, int $endTime, string $outboxUri, array $freeBusyUris): array {
|
||||
// get all blocks of time that are still free
|
||||
// so if there is an appointment from 10 to 11am, we would return the
|
||||
// slot from 9am to 10am and the slot from 11am to 5pm
|
||||
// IManager::search auf calendar in general - empty pattern
|
||||
return [
|
||||
['start' => $startTime, 'end' => $endTime]
|
||||
];
|
||||
}
|
||||
|
||||
public function findBookedSlotsAmount(int $id, int $unixStartDateTime, int $unixEndDateTime): int {
|
||||
// return amount of booked slot
|
||||
// IManager::search auf X-NC-Appts-Id
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function parseRRule($rrule) {
|
||||
return ['start' => '', 'end', ''];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use Recurr\Exception\InvalidRRule;
|
||||
use Recurr\Exception\InvalidWeekday;
|
||||
use Recurr\Recurrence;
|
||||
use Recurr\Rule;
|
||||
use Recurr\Transformer\ArrayTransformer;
|
||||
use Recurr\Transformer\ArrayTransformerConfig;
|
||||
|
||||
class Booking {
|
||||
|
||||
/** @var AppointmentConfig */
|
||||
private $appointmentConfig;
|
||||
|
||||
/** @var int */
|
||||
private $startTime;
|
||||
|
||||
/** @var int */
|
||||
private $endTime;
|
||||
|
||||
/** @var Slot[] */
|
||||
private $slots;
|
||||
|
||||
public function __construct(AppointmentConfig $appointmentConfig, int $startTime, int $endTime, array $slots = []) {
|
||||
$this->appointmentConfig = $appointmentConfig;
|
||||
$this->startTime = $startTime;
|
||||
$this->endTime = $endTime;
|
||||
$this->slots = $slots;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getStartTime(): int {
|
||||
return $this->startTime;
|
||||
}
|
||||
|
||||
// Trait maybe?
|
||||
public function getStartTimeDTObj() : DateTimeImmutable {
|
||||
return (new DateTimeImmutable())->setTimestamp($this->startTime);
|
||||
}
|
||||
|
||||
public function getEndTimeDTObj() : DateTimeImmutable {
|
||||
return (new DateTimeImmutable())->setTimestamp($this->endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $startTime
|
||||
*/
|
||||
public function setStartTime(int $startTime): void {
|
||||
$this->startTime = $startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getEndTime(): int {
|
||||
return $this->endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $endTime
|
||||
*/
|
||||
public function setEndTime(int $endTime): void {
|
||||
$this->endTime = $endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Slot[]
|
||||
*/
|
||||
public function getSlots(): array {
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Slot[] $slots
|
||||
*/
|
||||
public function setSlots(array $slots): void {
|
||||
$this->slots = $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AppointmentConfig
|
||||
*/
|
||||
public function getAppointmentConfig(): AppointmentConfig {
|
||||
return $this->appointmentConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AppointmentConfig $appointmentConfig
|
||||
*/
|
||||
public function setAppointmentConfig(AppointmentConfig $appointmentConfig): void {
|
||||
$this->appointmentConfig = $appointmentConfig;
|
||||
}
|
||||
|
||||
public function generateSlots(): array {
|
||||
$slots = [];
|
||||
$unixStartTime = $this->getStartTime();
|
||||
$length = $this->getAppointmentConfig()->getTotalLength()*60;
|
||||
while(($unixStartTime + $length) <= $this->getEndTime() ) {
|
||||
$slots[] = new Slot($unixStartTime, $unixStartTime+$this->getAppointmentConfig()->getTotalLength()*60);
|
||||
$unixStartTime += $this->getAppointmentConfig()->getIncrement()*60;
|
||||
}
|
||||
$this->slots = $slots;
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $booked
|
||||
* @return int
|
||||
*/
|
||||
public function getAvailableSlotsAmount(int $booked): int {
|
||||
return ($this->appointmentConfig->getDailyMax() !== null) ? $this->appointmentConfig->getDailyMax() - $booked : 99999;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self;
|
||||
*/
|
||||
public function parseRRule(): self {
|
||||
try {
|
||||
// RRule Array Transformer does not work with constraints atm
|
||||
// so this is (kind of) superfluous
|
||||
$startDT = $this->getStartTimeDTObj();
|
||||
$endDT = $this->getEndTimeDTObj();
|
||||
// force UTC
|
||||
$startDT->setTimezone(new \DateTimeZone('UTC'));
|
||||
$endDT->setTimezone(new \DateTimeZone('UTC'));
|
||||
$rule = new Rule($this->appointmentConfig->getAvailability());
|
||||
} catch (InvalidRRule $e) {
|
||||
$this->slots = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
$config = new ArrayTransformerConfig();
|
||||
$config->enableLastDayOfMonthFix();
|
||||
|
||||
$transformer = new ArrayTransformer();
|
||||
$transformer->setConfig($config);
|
||||
|
||||
try {
|
||||
$collection = $transformer->transform($rule);
|
||||
} catch (InvalidWeekday $e) {
|
||||
// throw an error here?
|
||||
$this->slots = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->slots = $collection->map(function (Recurrence $slot) {
|
||||
$start = $slot->getStart()->getTimestamp();
|
||||
$end = $start + ($this->appointmentConfig->getTotalLength() * 60);
|
||||
return new Slot($start, $end);
|
||||
})->toArray();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use OC\Calendar\CalendarQuery;
|
||||
use OCA\Calendar\Db\AppointmentConfigMapper;
|
||||
use OCP\Calendar\IManager;
|
||||
|
||||
class BookingService {
|
||||
|
||||
/** @var IManager */
|
||||
private $manager;
|
||||
|
||||
/** @var AppointmentConfigMapper */
|
||||
private $mapper;
|
||||
|
||||
public function __construct(IManager $manager,
|
||||
AppointmentConfigMapper $mapper) {
|
||||
$this->manager = $manager;
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
// CREATE
|
||||
public function book(string $calendarData) {
|
||||
// use new ICreateFromString::create method
|
||||
}
|
||||
|
||||
public function getBookingInformation(string $token) {
|
||||
// unmarshal token for ID would be an option too
|
||||
// this also returns all bookings for this token
|
||||
// needs a unique identifier - X-NC-USERID or something?
|
||||
$config = $this->mapper->findByToken($token);
|
||||
$query = $this->manager->newQuery($config->getPrincipalUri());
|
||||
$query->addSearchCalendar($config->getTargetCalendarUri());
|
||||
return $this->manager->searchForPrincipal($query);
|
||||
}
|
||||
|
||||
public function getSlots(Booking $booking): array {
|
||||
$bookedSlots = $this->findBookedSlotsAmount($booking);
|
||||
|
||||
// negotiate available slots
|
||||
if ($booking->getAvailableSlotsAmount($bookedSlots) <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// decide if we want to use the complete 24 hour period to intersect via:
|
||||
// $booking->generateSlots();
|
||||
// Remove unavailable slots via comparing with RRule
|
||||
$booking->parseRRule();
|
||||
|
||||
// remove conflicting slots via calendar free busy
|
||||
$booking = $this->getCalendarFreeTimeblocks($booking);
|
||||
return $booking->getSlots();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Booking $booking
|
||||
* @return Booking
|
||||
*
|
||||
* Check if slot is conflicting with existing appointments
|
||||
*/
|
||||
public function getCalendarFreeTimeblocks(Booking $booking): Booking {
|
||||
$query = $this->manager->newQuery($booking->getAppointmentConfig()->getPrincipalUri());
|
||||
$query->addSearchCalendar($booking->getAppointmentConfig()->getTargetCalendarUri());
|
||||
|
||||
if (!empty($booking->getAppointmentConfig()->getCalendarFreebusyUris())) {
|
||||
foreach ($booking->getAppointmentConfig()->getCalendarFreebusyUris() as $uri) {
|
||||
$query->addSearchCalendar($uri);
|
||||
}
|
||||
}
|
||||
|
||||
$slots = $booking->getSlots();
|
||||
foreach ($slots as $k => $slot) {
|
||||
$query->setTimerangeStart($slot->getStartTimeDTObj());
|
||||
$query->setTimerangeEnd($slot->getEndTimeDTObj());
|
||||
// cache the query maybe? or maybe run everything at once?
|
||||
$events = $this->manager->searchForPrincipal($query);
|
||||
if (!empty($events)) {
|
||||
unset($slots[$k]);
|
||||
}
|
||||
}
|
||||
$booking->setSlots($slots);
|
||||
return $booking;
|
||||
}
|
||||
|
||||
public function findBookedSlotsAmount(Booking $booking): int {
|
||||
/** @var CalendarQuery $query */
|
||||
$query = $this->manager->newQuery($booking->getAppointmentConfig()->getPrincipalUri());
|
||||
$query->addSearchCalendar($booking->getAppointmentConfig()->getTargetCalendarUri());
|
||||
$query->addSearchProperty('X-NC-APPOINTMENT');
|
||||
$query->setSearchPattern($booking->getAppointmentConfig()->getToken());
|
||||
$query->setTimerangeStart($booking->getStartTimeDTObj());
|
||||
$query->setTimerangeEnd($booking->getEndTimeDTObj());
|
||||
$events = $this->manager->searchForPrincipal($query);
|
||||
return count($events);
|
||||
}
|
||||
|
||||
// Update
|
||||
public function updateBooking() {
|
||||
// noop for now? we don't support a public update method at the moment
|
||||
}
|
||||
|
||||
// Delete
|
||||
public function delete() {
|
||||
// this would be a cancel request to ICreateFromString::create()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
class Slot {
|
||||
private $start;
|
||||
private $end;
|
||||
|
||||
public function __construct(int $start, int $end) {
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
public function getStartTime(): int {
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
public function setStartTime(int $start): void {
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
public function getEndTime(): int {
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
public function setEndTime(int $end): void {
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
public function getStartTimeDTObj() : DateTimeImmutable {
|
||||
return (new DateTimeImmutable())->setTimestamp($this->start);
|
||||
}
|
||||
|
||||
public function getEndTimeDTObj() : DateTimeImmutable {
|
||||
return (new DateTimeImmutable())->setTimestamp($this->end);
|
||||
}
|
||||
|
||||
public function isViable(int $start, int $end): bool {
|
||||
return !($this->start > $start || $this->end < $end);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OC\AppFramework\Bootstrap\Coordinator;
|
||||
use OC\AppFramework\Bootstrap\RegistrationContext;
|
||||
use OC\AppFramework\Bootstrap\ServiceRegistration;
|
||||
use OC\Calendar\Manager;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\CalendarProvider;
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Db\AppointmentConfigMapper;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class BookingServiceTest extends TestCase {
|
||||
|
||||
/** @var AppointmentConfigMapper|MockObject */
|
||||
private $mapper;
|
||||
|
||||
/** @var BookingService */
|
||||
private $service;
|
||||
|
||||
/** @var mixed|Manager|MockObject */
|
||||
private $manager;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$backend = \OC::$server->get(CalDavBackend::class);
|
||||
$container = \OC::$server->get(\Psr\Container\ContainerInterface::class);
|
||||
$l10n = $this->createMock(IL10N::class);
|
||||
$conf = $this->createMock(IConfig::class);
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
$coordinator = $this->createConfiguredMock(Coordinator::class, [
|
||||
'getRegistrationContext' => $this->createConfiguredMock(RegistrationContext::class, [
|
||||
'getCalendarProviders' => [ new ServiceRegistration('calendar', CalendarProvider::class) ]
|
||||
])
|
||||
]);
|
||||
$this->manager = new Manager($coordinator, $container, $logger);
|
||||
$this->mapper = $this->createMock(AppointmentConfigMapper::class);
|
||||
$this->service = new BookingService(
|
||||
$this->manager,
|
||||
$this->mapper
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCalendarFreeTimeblocks() {
|
||||
$appointmentConfig = new AppointmentConfig();
|
||||
$appointmentConfig->setPrincipalUri('principals/users/test');
|
||||
$appointmentConfig->setTargetCalendarUri('personal');
|
||||
$appointmentConfig ->setLength(30);
|
||||
$appointmentConfig ->setIncrement(15);
|
||||
$appointmentConfig ->setPreparationDuration(15);
|
||||
$appointmentConfig ->setFollowupDuration(15);
|
||||
$appointmentConfig ->setAvailability("RRULE:FREQ=MINUTELY;INTERVAL=15;WKST=MO;BYDAY=MO;BYHOUR=8,9,10,11");
|
||||
$booking = new Booking($appointmentConfig, time(), (time() + 84600));
|
||||
$booking->parseRRule();
|
||||
$this->service->getCalendarFreeTimeblocks($booking);
|
||||
}
|
||||
|
||||
public function testGetCalendarCount() {
|
||||
$appointmentConfig = new AppointmentConfig();
|
||||
$appointmentConfig->setPrincipalUri('principals/users/admin');
|
||||
$appointmentConfig->setTargetCalendarUri('personal');
|
||||
$appointmentConfig->setToken('1');
|
||||
$appointmentConfig ->setLength(30);
|
||||
$appointmentConfig ->setIncrement(15);
|
||||
$appointmentConfig ->setPreparationDuration(15);
|
||||
$appointmentConfig ->setFollowupDuration(15);
|
||||
$appointmentConfig ->setAvailability("RRULE:FREQ=MINUTELY;INTERVAL=15;WKST=MO;BYDAY=MO;BYHOUR=8,9,10,11");
|
||||
$booking = new Booking($appointmentConfig, time(), (time() + 84600));
|
||||
$count = $this->service->findBookedSlotsAmount($booking);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Calendar\Tests\Integration\Db;
|
||||
|
||||
use BadFunctionCallException;
|
||||
use ChristophWurst\Nextcloud\Testing\DatabaseTransaction;
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use InvalidArgumentException;
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Db\AppointmentConfigMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class AppointmentMapperTest extends TestCase {
|
||||
use DatabaseTransaction;
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $db;
|
||||
|
||||
/** @var AppointmentConfigMapper */
|
||||
private $mapper;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->db = \OC::$server->getDatabaseConnection();
|
||||
$this->mapper = new AppointmentConfigMapper(
|
||||
$this->db
|
||||
);
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$delete = $qb->delete($this->mapper->getTableName());
|
||||
$delete->execute();
|
||||
}
|
||||
|
||||
public function testFindByIdNoData() {
|
||||
$this->expectException(DoesNotExistException::class);
|
||||
$this->mapper->findByIdForUser(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFindByIdNoData
|
||||
*/
|
||||
public function testFindById() {
|
||||
$appointment = new AppointmentConfig();
|
||||
$appointment->setName('Test 2');
|
||||
$appointment->setDescription('Test Description');
|
||||
$appointment->setIncrement(15);
|
||||
$appointment->setLength(60);
|
||||
$appointment->setTargetCalendarUri('testuri');
|
||||
$appointment->setVisibility(AppointmentConfig::VISIBILITY_PUBLIC);
|
||||
$appointment->setUserId('testuser');
|
||||
|
||||
$appointment = $this->mapper->insert($appointment);
|
||||
$id = $appointment->getId();
|
||||
$appointment = $this->mapper->findById($id);
|
||||
|
||||
$this->assertObjectHasAttribute('name', $appointment);
|
||||
$this->assertEquals('Test 2', $appointment->getName());
|
||||
$this->assertObjectHasAttribute('description', $appointment);
|
||||
$this->assertEquals('Test Description', $appointment->getDescription());
|
||||
$this->assertObjectHasAttribute('increment', $appointment);
|
||||
$this->assertEquals(15, $appointment->getIncrement());
|
||||
$this->assertObjectHasAttribute('length', $appointment);
|
||||
$this->assertEquals(60, $appointment->getLength());
|
||||
$this->assertObjectHasAttribute('calendarUri', $appointment);
|
||||
$this->assertEquals('testuri', $appointment->getCalendarUri());
|
||||
$this->assertObjectHasAttribute('visibility', $appointment);
|
||||
$this->assertEquals(AppointmentConfig::VISIBILITY_PUBLIC, $appointment->getVisibility());
|
||||
$this->assertObjectHasAttribute('userId', $appointment);
|
||||
$this->assertEquals('testuser', $appointment->getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFindByIdNoData
|
||||
*/
|
||||
public function testInsertFromData() {
|
||||
$data = [
|
||||
'name' => 'Test 1',
|
||||
'description' => 'Test Description',
|
||||
'increment' => 15,
|
||||
'length' => 60,
|
||||
'calendarUri' => 'testuri',
|
||||
'visibility' => AppointmentConfig::VISIBILITY_PUBLIC,
|
||||
'userId' => 'testuser'
|
||||
];
|
||||
|
||||
$appointment = $this->mapper->insertFromData($data);
|
||||
|
||||
$this->assertObjectHasAttribute('name', $appointment);
|
||||
$this->assertEquals('Test 1', $appointment->getName());
|
||||
$this->assertObjectHasAttribute('description', $appointment);
|
||||
$this->assertEquals('Test Description', $appointment->getDescription());
|
||||
$this->assertObjectHasAttribute('increment', $appointment);
|
||||
$this->assertEquals(15, $appointment->getIncrement());
|
||||
$this->assertObjectHasAttribute('length', $appointment);
|
||||
$this->assertEquals(60, $appointment->getLength());
|
||||
$this->assertObjectHasAttribute('calendarUri', $appointment);
|
||||
$this->assertEquals('testuri', $appointment->getCalendarUri());
|
||||
$this->assertObjectHasAttribute('visibility', $appointment);
|
||||
$this->assertEquals(AppointmentConfig::VISIBILITY_PUBLIC, $appointment->getVisibility());
|
||||
$this->assertObjectHasAttribute('userId', $appointment);
|
||||
$this->assertEquals('testuser', $appointment->getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFindByIdNoData
|
||||
*/
|
||||
public function testInsertFromDataBadFunctionCallException() {
|
||||
// $data = [
|
||||
// 'fhskjhfkjsdhj' => 'Failing'
|
||||
// ];
|
||||
// $this->expectException(BadFunctionCallException::class);
|
||||
// $this->mapper->insert($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFindByIdNoData
|
||||
*/
|
||||
public function testUpdateFromData() {
|
||||
$appointment = new AppointmentConfig();
|
||||
$appointment->setName('Test 3');
|
||||
$appointment->setDescription('Test Description');
|
||||
$appointment->setIncrement(15);
|
||||
$appointment->setLength(60);
|
||||
$appointment->setTargetCalendarUri('testuri');
|
||||
$appointment->setVisibility(AppointmentConfig::VISIBILITY_PUBLIC);
|
||||
$appointment->setUserId('testuser');
|
||||
$appointment = $this->mapper->insert($appointment);
|
||||
$id = $appointment->getId();
|
||||
// $data = [
|
||||
// 'id' => $id,
|
||||
// 'name' => 'Test 9001',
|
||||
// 'description' => 'Test Description updated',
|
||||
// 'increment' => 15,
|
||||
// 'length' => 60,
|
||||
// 'calendarUri' => 'testuri',
|
||||
// 'visibility' => AppointmentConfig::VISIBILITY_PUBLIC,
|
||||
// 'userId' => 'testuser',
|
||||
// 'followupDuration' => 100
|
||||
// ];
|
||||
|
||||
// $appointment = $this->mapper->update($data);
|
||||
|
||||
$this->assertObjectHasAttribute('id', $appointment);
|
||||
$this->assertEquals($id, $appointment->getId());
|
||||
$this->assertObjectHasAttribute('name', $appointment);
|
||||
$this->assertEquals('Test 9001', $appointment->getName());
|
||||
$this->assertObjectHasAttribute('description', $appointment);
|
||||
$this->assertEquals('Test Description updated', $appointment->getDescription());
|
||||
$this->assertObjectHasAttribute('increment', $appointment);
|
||||
$this->assertEquals(15, $appointment->getIncrement());
|
||||
$this->assertObjectHasAttribute('length', $appointment);
|
||||
$this->assertEquals(60, $appointment->getLength());
|
||||
$this->assertObjectHasAttribute('calendarUri', $appointment);
|
||||
$this->assertEquals('testuri', $appointment->getCalendarUri());
|
||||
$this->assertObjectHasAttribute('visibility', $appointment);
|
||||
$this->assertEquals(AppointmentConfig::VISIBILITY_PUBLIC, $appointment->getVisibility());
|
||||
$this->assertObjectHasAttribute('userId', $appointment);
|
||||
$this->assertEquals('testuser', $appointment->getUserId());
|
||||
$this->assertObjectHasAttribute('followupDuration', $appointment);
|
||||
$this->assertEquals(100, $appointment->getFollowupDuration());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFindByIdNoData
|
||||
*/
|
||||
public function testUpdateFromDataInvalidArgumentException() {
|
||||
// $data = [
|
||||
// 'name' => 'Test 9001',
|
||||
// 'description' => 'Test Description updated',
|
||||
// 'increment' => 15,
|
||||
// 'length' => 60,
|
||||
// 'calendarUri' => 'testuri',
|
||||
// 'visibility' => AppointmentConfig::VISIBILITY_PUBLIC,
|
||||
// 'userId' => 'testuser',
|
||||
// 'followupDuration' => 100
|
||||
// ];
|
||||
//
|
||||
// $this->expectException(InvalidArgumentException::class);
|
||||
// $this->mapper->update($data);
|
||||
}
|
||||
|
||||
public function testFindAllForUser():void {
|
||||
$appointment = new AppointmentConfig();
|
||||
$appointment->setName('Test 2');
|
||||
$appointment->setDescription('Test Description');
|
||||
$appointment->setIncrement(15);
|
||||
$appointment->setLength(60);
|
||||
$appointment->setTargetCalendarUri('testuri');
|
||||
$appointment->setVisibility(AppointmentConfig::VISIBILITY_PUBLIC);
|
||||
$appointment->setUserId('testuser');
|
||||
|
||||
$this->mapper->insert($appointment);
|
||||
$appointments = $this->mapper->findAllForUser('testuser');
|
||||
|
||||
$this->assertNotEmpty($appointments);
|
||||
|
||||
foreach ($appointments as $appointment) {
|
||||
$this->assertObjectHasAttribute('userId', $appointment);
|
||||
$this->assertEquals('testuser', $appointment->getUserId());
|
||||
}
|
||||
}
|
||||
|
||||
public function testDeleteById():void {
|
||||
$appointment = new AppointmentConfig();
|
||||
$appointment->setName('Test 2');
|
||||
$appointment->setDescription('Test Description');
|
||||
$appointment->setIncrement(15);
|
||||
$appointment->setLength(60);
|
||||
$appointment->setTargetCalendarUri('testuri');
|
||||
$appointment->setVisibility(AppointmentConfig::VISIBILITY_PUBLIC);
|
||||
$appointment->setUserId('testuser');
|
||||
|
||||
$appointment = $this->mapper->insert($appointment);
|
||||
|
||||
$row = $this->mapper->deleteById($appointment->getId(), $appointment->getUserId());
|
||||
|
||||
$this->assertEquals(1, $row);
|
||||
|
||||
$this->expectException(DoesNotExistException::class);
|
||||
$this->mapper->findById($appointment->getId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Controller;
|
||||
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Exception\ServiceException;
|
||||
use OCA\Calendar\Service\AppointmentConfigService;
|
||||
use OCP\Contacts\IManager;
|
||||
use OCP\IInitialStateService;
|
||||
use OCP\IRequest;
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCP\IUser;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class AppointmentsControllerTest extends TestCase {
|
||||
|
||||
/** @var string */
|
||||
protected $appName;
|
||||
|
||||
/** @var IRequest|MockObject */
|
||||
protected $request;
|
||||
|
||||
/** @var IManager|MockObject */
|
||||
protected $manager;
|
||||
|
||||
/** @var IInitialStateService|MockObject */
|
||||
protected $initialState;
|
||||
|
||||
/** @var IUser|MockObject */
|
||||
protected $user;
|
||||
|
||||
/** @var AppointmentConfigService|MockObject */
|
||||
protected $service;
|
||||
|
||||
/** @var AppointmentConfigController */
|
||||
protected $controller;
|
||||
|
||||
protected function setUp():void {
|
||||
parent::setUp();
|
||||
|
||||
$this->appName = 'calendar';
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->manager = $this->createMock(IManager::class);
|
||||
$this->user = $this->createConfiguredMock(IUser::class, [
|
||||
'getUID' => 'testuser'
|
||||
]);
|
||||
$this->initialState = $this->createMock(IInitialStateService::class);
|
||||
$this->service = $this->createMock(AppointmentConfigService::class);
|
||||
$this->controller = new AppointmentConfigController(
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->initialState,
|
||||
$this->user,
|
||||
$this->service
|
||||
);
|
||||
}
|
||||
|
||||
// public function testIndex(): void {
|
||||
// $appointments = [new AppointmentConfig()];
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('getAllAppointmentConfigurations')
|
||||
// ->with($this->user->getUID())
|
||||
// ->willReturn($appointments);
|
||||
//
|
||||
// $this->initialState->expects($this->once())
|
||||
// ->method('provideInitialState')
|
||||
// ->with(
|
||||
// $this->appName,
|
||||
// 'appointments',
|
||||
// $appointments
|
||||
// );
|
||||
//
|
||||
// $this->controller->index('user');
|
||||
// }
|
||||
//
|
||||
// public function testIndexException(): void {
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('getAllAppointmentConfigurations')
|
||||
// ->with($this->user->getUID())
|
||||
// ->willThrowException(new ServiceException());
|
||||
//
|
||||
// $this->initialState->expects($this->once())
|
||||
// ->method('provideInitialState')
|
||||
// ->with(
|
||||
// $this->appName,
|
||||
// 'appointments',
|
||||
// []
|
||||
// );
|
||||
//
|
||||
// $this->controller->index('user');
|
||||
// }
|
||||
//
|
||||
// public function testCreate():void {
|
||||
// $data = [];
|
||||
// $appointment = new AppointmentConfig([]);
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('create')
|
||||
// ->with($data)
|
||||
// ->willReturn($appointment);
|
||||
//
|
||||
// $response = $this->controller->create($data);
|
||||
//
|
||||
// $this->assertEquals(
|
||||
// [
|
||||
// 'status' => 'success',
|
||||
// 'data' => $appointment
|
||||
// ], $response->getData());
|
||||
// $this->assertEquals(200, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testCreateException():void {
|
||||
// $data = [];
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('create')
|
||||
// ->with($data)
|
||||
// ->willThrowException(new ServiceException());
|
||||
//
|
||||
// $response = $this->controller->create($data);
|
||||
//
|
||||
// $this->assertEquals(500, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testShow():void {
|
||||
// $id = 1;
|
||||
// $appointment = new AppointmentConfig();
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with(1)
|
||||
// ->willReturn($appointment);
|
||||
//
|
||||
// $response = $this->controller->show($id);
|
||||
//
|
||||
// $this->assertEquals(
|
||||
// [
|
||||
// 'status' => 'success',
|
||||
// 'data' => $appointment
|
||||
// ], $response->getData());
|
||||
// $this->assertEquals(200, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testShowException():void {
|
||||
// $id = 1;
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with(1)
|
||||
// ->willThrowException(new ServiceException());
|
||||
//
|
||||
// $response = $this->controller->show($id);
|
||||
//
|
||||
// $this->assertEquals(500, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testUpdate():void {
|
||||
// $data = [];
|
||||
// $appointment = new AppointmentConfig();
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('update')
|
||||
// ->with($data)
|
||||
// ->willReturn($appointment);
|
||||
//
|
||||
// $response = $this->controller->update([]);
|
||||
//
|
||||
// $this->assertEquals(
|
||||
// [
|
||||
// 'status' => 'success',
|
||||
// 'data' => $appointment
|
||||
// ], $response->getData());
|
||||
// $this->assertEquals(200, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testUpdateException():void {
|
||||
// $data = [];
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('update')
|
||||
// ->with($data)
|
||||
// ->willThrowException(new ServiceException());
|
||||
//
|
||||
// $response = $this->controller->update($data);
|
||||
//
|
||||
// $this->assertEquals(500, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testDelete():void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('delete')
|
||||
// ->with(1);
|
||||
//
|
||||
// $response = $this->controller->delete($id);
|
||||
//
|
||||
// $this->assertEquals(
|
||||
// [
|
||||
// 'status' => 'success',
|
||||
// 'data' => null
|
||||
// ], $response->getData());
|
||||
// $this->assertEquals(200, $response->getStatus());
|
||||
// }
|
||||
//
|
||||
// public function testDeleteException():void {
|
||||
// $id = 1;
|
||||
// $this->service->expects($this->once())
|
||||
// ->method('delete')
|
||||
// ->with(1)
|
||||
// ->willThrowException(new ServiceException());
|
||||
//
|
||||
// $response = $this->controller->delete($id);
|
||||
//
|
||||
// $this->assertEquals(403, $response->getStatus());
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\DB\Exception;
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Db\AppointmentConfigMapper;
|
||||
use OCA\Calendar\Exception\ServiceException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class AppointmentServiceTest extends TestCase {
|
||||
|
||||
/** @var AppointmentConfigMapper|MockObject */
|
||||
private $mapper;
|
||||
|
||||
/** @var AppointmentConfigService */
|
||||
private $service;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->mapper = $this->createMock(AppointmentConfigMapper::class);
|
||||
$this->service = new AppointmentConfigService(
|
||||
$this->mapper
|
||||
);
|
||||
}
|
||||
|
||||
// public function testGetAllConfigurations(){
|
||||
// $user = 'testuser';
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findAllForUser')
|
||||
// ->with($user)
|
||||
// ->willReturn([new AppointmentConfig()]);
|
||||
//
|
||||
// $this->service->getAllAppointmentConfigurations($user);
|
||||
// }
|
||||
//
|
||||
// public function testGetAllConfigurationsException(){
|
||||
// $user = 'testuser';
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findAllForUser')
|
||||
// ->with($user)
|
||||
// ->willThrowException(new Exception());
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->getAllAppointmentConfigurations($user);
|
||||
// }
|
||||
//
|
||||
// public function testDelete(): void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('deleteById')
|
||||
// ->with($id);
|
||||
//
|
||||
// $this->service->delete($id);
|
||||
// }
|
||||
//
|
||||
// public function testDeleteException(): void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('deleteById')
|
||||
// ->with($id)
|
||||
// ->willThrowException(new Exception());
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->delete($id);
|
||||
// }
|
||||
//
|
||||
// public function testUpdate(): void {
|
||||
// $data = [];
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('updateFromData')
|
||||
// ->with($data)
|
||||
// ->willReturn(new AppointmentConfig());
|
||||
//
|
||||
// $this->service->update($data);
|
||||
// }
|
||||
//
|
||||
// public function testUpdateException(): void {
|
||||
// $data = [];
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('updateFromData')
|
||||
// ->with($data)
|
||||
// ->willThrowException(new Exception());
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->update($data);
|
||||
// }
|
||||
//
|
||||
// public function testFindById(): void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with($id)
|
||||
// ->willReturn(new AppointmentConfig());
|
||||
//
|
||||
// $this->service->findById($id);
|
||||
// }
|
||||
//
|
||||
// public function testFindByIdException(): void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with($id)
|
||||
// ->willThrowException(new Exception());
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->findById($id);
|
||||
// }
|
||||
//
|
||||
// public function testFindByIdDoesNotExistException(): void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with($id)
|
||||
// ->willThrowException(new DoesNotExistException(''));
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->findById($id);
|
||||
// }
|
||||
//
|
||||
// public function testFindByIdMultipleObjectsReturnedException(): void {
|
||||
// $id = 1;
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with($id)
|
||||
// ->willThrowException(new MultipleObjectsReturnedException(''));
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->findById($id);
|
||||
// }
|
||||
//
|
||||
// public function testCreate(): void {
|
||||
// $data = [];
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('insertFromData')
|
||||
// ->with($data)
|
||||
// ->willReturn(new AppointmentConfig());
|
||||
//
|
||||
// $this->service->create($data);
|
||||
// }
|
||||
//
|
||||
// public function testCreateException(): void {
|
||||
// $data = [];
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('insertFromData')
|
||||
// ->with($data)
|
||||
// ->willThrowException(new Exception());
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->create($data);
|
||||
// }
|
||||
//
|
||||
// public function testIsNotInFuture() : void {
|
||||
// $id = 1;
|
||||
// $startDate = strtotime('-1 day');
|
||||
// $endDate = time();
|
||||
//
|
||||
// $this->mapper->expects($this->never())
|
||||
// ->method('findById');
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->getSlots($id, $startDate, $endDate, '');
|
||||
// }
|
||||
//
|
||||
// public function testIdDoesNotExist():void {
|
||||
// $id = 1;
|
||||
// $appointment = new AppointmentConfig();
|
||||
// $appointment->setLength(0);
|
||||
// $appointment->setIncrement(0);
|
||||
//
|
||||
// // use one day in the future
|
||||
// $startDate = strtotime('+1 day');
|
||||
// $endDate = strtotime('+31 hours');
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with($id)
|
||||
// ->willThrowException(new ServiceException());
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->getSlots($id, $startDate, $endDate, '');
|
||||
// }
|
||||
//
|
||||
// public function testGetSlotsNoLength():void {
|
||||
// $id = 1;
|
||||
// $appointment = new AppointmentConfig();
|
||||
// $appointment->setLength(0);
|
||||
// $appointment->setIncrement(0);
|
||||
//
|
||||
// $startDate = strtotime('+1 day');
|
||||
// $endDate = strtotime('+31 hours');// 7 hour timespan
|
||||
//
|
||||
// $this->mapper->expects($this->once())
|
||||
// ->method('findById')
|
||||
// ->with($id)
|
||||
// ->willReturn($appointment);
|
||||
//
|
||||
// $this->expectException(ServiceException::class);
|
||||
// $this->service->getSlots($id, $startDate, $endDate, '');
|
||||
// }
|
||||
|
||||
// public function testGetSlots():void {
|
||||
// $appointment = new AppointmentConfig();
|
||||
// /** every 15 minutes */
|
||||
// $appointment->setIncrement(15);
|
||||
// /** 60 minutes long */
|
||||
// $appointment->setLength(60);
|
||||
//
|
||||
// $startDate = strtotime('+1 day');
|
||||
// $endDate = strtotime('+31 hours');// 7 hour timespan
|
||||
//
|
||||
//
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OC\Calendar\CalendarQuery;
|
||||
use OC\Calendar\Manager;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\CalendarProvider;
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use OCA\Calendar\Db\AppointmentConfigMapper;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class BookingServiceTest extends TestCase {
|
||||
|
||||
/** @var AppointmentConfigMapper|MockObject */
|
||||
private $mapper;
|
||||
|
||||
/** @var BookingService */
|
||||
private $service;
|
||||
|
||||
/** @var mixed|Manager|MockObject */
|
||||
private $manager;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$backend = \OC::$server->get(CalDavBackend::class);
|
||||
$calendarProvider = new CalendarProvider($backend);
|
||||
$this->manager = \OC::$server->get(Manager::class);
|
||||
$this->mapper = $this->createMock(AppointmentConfigMapper::class);
|
||||
$this->service = new BookingService(
|
||||
$this->manager,
|
||||
$this->mapper
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCalendarFreeTimeblocks() {
|
||||
$appointmentConfig = new AppointmentConfig();
|
||||
$appointmentConfig->setUserId('admin'); // or something else
|
||||
$appointmentConfig->setTargetCalendarUri('calendars/admin/personal/');
|
||||
$booking = new Booking($appointmentConfig, time(), (time()+3600) );
|
||||
$calendarQuery = new CalendarQuery($appointmentConfig->getPrincipalUri());
|
||||
$booking->setAppointmentConfig($appointmentConfig);
|
||||
$this->manager->expects($this->once())
|
||||
->method('newQuery')
|
||||
->with($appointmentConfig->getPrincipalUri())
|
||||
->willReturn($calendarQuery);
|
||||
$this->manager->expects($this->once())
|
||||
->method('searchForPrincipal')
|
||||
->with($calendarQuery)
|
||||
->willReturn(null);
|
||||
$this->service->getCalendarFreeTimeblocks($booking);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* @author Anna Larch <anna.larch@gmx.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Calendar\Service\Appointments;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCA\Calendar\Db\AppointmentConfig;
|
||||
use Recurr\Exception\InvalidRRule;
|
||||
use Recurr\Exception\InvalidWeekday;
|
||||
use Recurr\Recurrence;
|
||||
use Recurr\Rule;
|
||||
use Recurr\Transformer\ArrayTransformer;
|
||||
use Recurr\Transformer\ArrayTransformerConfig;
|
||||
|
||||
class BookingTest extends TestCase {
|
||||
|
||||
/** @var Booking */
|
||||
private $booking;
|
||||
/** @var AppointmentConfig */
|
||||
private $appointmentConfig;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->appointmentConfig = new AppointmentConfig();
|
||||
$this->booking = new Booking($this->appointmentConfig,strtotime('midnight'), (strtotime('midnight') + 84000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Booking::getAppointmentConfig()
|
||||
* @covers Booking::setAppointmentConfig()
|
||||
*/
|
||||
public function testSetAppointmentConfig(): void {
|
||||
$this->appointmentConfig = new AppointmentConfig();
|
||||
$this->booking->setAppointmentConfig($this->appointmentConfig);
|
||||
|
||||
$this->assertEquals($this->booking->getAppointmentConfig(), $this->appointmentConfig);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue