enh(appointments): add moar debug logging and overwrite app id

Signed-off-by: Anna Larch <anna@nextcloud.com>
This commit is contained in:
Anna Larch 2024-01-29 23:38:00 +01:00
parent 1c6eb45b7a
commit 9d54b96c24
9 changed files with 74 additions and 31 deletions

View File

@ -31,6 +31,7 @@ use DateTimeImmutable;
use DateTimeZone;
use OCA\Calendar\Db\AppointmentConfig;
use OCP\AppFramework\Utility\ITimeFactory;
use Psr\Log\LoggerInterface;
use function ceil;
use function max;
use function min;
@ -39,7 +40,7 @@ class AvailabilityGenerator {
/** @var ITimeFactory */
private $timeFactory;
public function __construct(ITimeFactory $timeFactory) {
public function __construct(ITimeFactory $timeFactory, private LoggerInterface $logger) {
$this->timeFactory = $timeFactory;
}
@ -79,11 +80,17 @@ class AvailabilityGenerator {
// If we reach this state then there are no available dates anymore
if ($latestEnd <= $earliestStart) {
$this->logger->debug('Appointment config ' . $config->getToken() . ' has {latestEnd} as latest end but {earliestStart} as earliest start. No slots available.', [
'latestEnd' => $latestEnd,
'earliestStart' => $earliestStart,
'app' => 'calendar-appointments'
]);
return [];
}
if (empty($config->getAvailability())) {
// No availability -> full time range is available
$this->logger->debug('Full time range available', ['app' => 'calendar-appointments']);
return [
new Interval($earliestStart, $latestEnd),
];
@ -95,6 +102,7 @@ class AvailabilityGenerator {
$slots = $availabilityRule['slots'];
$applicableSlots = $this->filterDates($start, $slots, $timeZone);
$this->logger->debug('Found ' . count($applicableSlots) . ' applicable slot(s) after date filtering', ['app' => 'calendar-appointments']);
$intervals = [];
foreach ($applicableSlots as $slot) {

View File

@ -142,13 +142,13 @@ class BookingService {
$this->mailService->sendBookingInformationEmail($booking, $config, $calendar);
$this->mailService->sendOrganizerBookingInformationEmail($booking, $config, $calendar);
} catch (ServiceException $e) {
$this->logger->info('Could not send booking emails after confirmation from user ' . $booking->getEmail(), ['exception' => $e]);
$this->logger->info('Could not send booking emails after confirmation from user ' . $booking->getEmail(), ['exception' => $e, 'app' => 'calendar-appointments']);
}
try {
$this->mailService->sendOrganizerBookingInformationNotification($booking, $config);
} catch (\InvalidArgumentException $e) {
$this->logger->warning('Could not send booking information notification after confirmation by user ' . $booking->getEmail(), ['exception' => $e]);
$this->logger->warning('Could not send booking information notification after confirmation by user ' . $booking->getEmail(), ['exception' => $e, 'app' => 'calendar-appointments']);
}
return $booking;
@ -203,10 +203,13 @@ class BookingService {
if ($config->getFutureLimit() !== null) {
/** @var int $maxEndTime */
$maxEndTime = time() + $config->getFutureLimit();
$this->logger->debug('Maximum end time: ' . $maxEndTime, ['app' => 'calendar-appointments']);
if ($startTime > $maxEndTime) {
$this->logger->debug('Start time is higher than maximum end time. Start time: ' . $startTime, ['app' => 'calendar-appointments']);
return [];
}
if ($endTime > $maxEndTime) {
$this->logger->debug('End time is higher than maximum end time. Setting end time to maximum end time. End time: ' . $endTime, ['app' => 'calendar-appointments']);
$endTime = $maxEndTime;
}
}
@ -224,7 +227,8 @@ class BookingService {
'availabilityIntervals' => count($availabilityIntervals),
'allPossibleSlots' => count($allPossibleSlots),
'filteredByDailyLimit' => count($filteredByDailyLimit),
'available' => count($available)
'available' => count($available),
'app' => 'calendar-appointments',
]);
return $available;

View File

@ -28,6 +28,7 @@ namespace OCA\Calendar\Service\Appointments;
use OCA\Calendar\Db\AppointmentConfig;
use OCP\Calendar\ICalendarQuery;
use OCP\Calendar\IManager;
use Psr\Log\LoggerInterface;
use function array_filter;
use function array_values;
use function count;
@ -36,7 +37,7 @@ class DailyLimitFilter {
/** @var IManager */
private $calendarManger;
public function __construct(IManager $calendarManger) {
public function __construct(IManager $calendarManger, private LoggerInterface $logger) {
$this->calendarManger = $calendarManger;
}
@ -47,6 +48,10 @@ class DailyLimitFilter {
* @return Interval[]
*/
public function filter(AppointmentConfig $config, array $slots): array {
$this->logger->debug('Slots before daily limit filtering:' . count($slots), ['app' => 'calendar-appointments']);
if(empty($slots)) {
return [];
}
// 0. If there is no limit then we don't have to filter anything
if ($config->getDailyMax() === null) {
return $slots;
@ -90,10 +95,13 @@ class DailyLimitFilter {
}
// 3. Filter out the slots that are on an unavailable day
return array_values(array_filter($slots, function (Interval $slot) use ($available): bool {
$available = array_values(array_filter($slots, function (Interval $slot) use ($available): bool {
$startOfDay = $slot->getStartAsObject()->setTime(0, 0, 0, 0);
$ts = $startOfDay->getTimestamp();
return $available[$ts];
}));
$this->logger->debug('Slots after daily limit filtering:' . count($available), ['app' => 'calendar-appointments']);
return $available;
}
}

View File

@ -54,6 +54,10 @@ class EventConflictFilter {
* @return Interval[]
*/
public function filter(AppointmentConfig $config, array $slots): array {
$this->logger->debug('Slots before event conflict filtering:' . count($slots), ['app' => 'calendar-appointments']);
if(empty($slots)) {
return [];
}
$query = $this->calendarManager->newQuery($config->getPrincipalUri());
foreach ($config->getCalendarFreebusyUrisAsArray() as $uri) {
$query->addSearchCalendar($uri);
@ -63,7 +67,7 @@ class EventConflictFilter {
$query->addType('VEVENT');
$preparationDuration = DateInterval::createFromDateString($config->getPreparationDuration() . ' seconds');
$followUpDuration = DateInterval::createFromDateString($config->getFollowupDuration() . ' seconds');
return array_filter($slots, function (Interval $slot) use ($followUpDuration, $preparationDuration, $query, $config): bool {
$available = array_filter($slots, function (Interval $slot) use ($followUpDuration, $preparationDuration, $query, $config): bool {
$query->setTimerangeStart($slot->getStartAsObject()->sub($preparationDuration));
$query->setTimerangeEnd($slot->getEndAsObject()->add($followUpDuration));
@ -81,7 +85,7 @@ class EventConflictFilter {
$this->logger->debug('Appointment config ' . $config->getToken() . ' is looking within {start} and {followup} in calendar {calendarUri}. Conflicting UIDs are {uids}', [
'start' => $slot->getStartAsObject()->sub($preparationDuration)->format(DateTimeInterface::ATOM),
'end' => $slot->getEndAsObject()->add($followUpDuration)->format(DateTimeInterface::ATOM),
'followup' => $slot->getEndAsObject()->add($followUpDuration)->format(DateTimeInterface::ATOM),
'calendarUri' => $config->getTargetCalendarUri(),
'uids' => implode(' : ', $uids)
]);
@ -89,5 +93,8 @@ class EventConflictFilter {
// If there is at least one event at this time then the slot is taken
return empty($objects);
});
$this->logger->debug('Slots after event conflict filtering:' . count($available), ['app' => 'calendar-appointments']);
return $available;
}
}

View File

@ -146,14 +146,14 @@ class MailService {
try {
$failed = $this->mailer->send($message);
if (count($failed) > 0) {
$this->logger->warning('Mail delivery failed for some recipients.');
$this->logger->warning('Mail delivery failed for some recipients.', ['app' => 'calendar-appointments']);
foreach ($failed as $fail) {
$this->logger->debug('Failed to deliver email to ' . $fail);
$this->logger->debug('Failed to deliver email to ' . $fail, ['app' => 'calendar-appointments']);
}
throw new ServiceException('Could not send mail for recipient(s) ' . implode(', ', $failed));
}
} catch (Exception $ex) {
$this->logger->error($ex->getMessage(), ['exception' => $ex]);
$this->logger->error($ex->getMessage(), ['exception' => $ex, 'app' => 'calendar-appointments']);
throw new ServiceException('Could not send mail: ' . $ex->getMessage(), $ex->getCode(), $ex);
}
}
@ -215,14 +215,14 @@ class MailService {
try {
$failed = $this->mailer->send($message);
if (count($failed) > 0) {
$this->logger->warning('Mail delivery failed for some recipients.');
$this->logger->warning('Mail delivery failed for some recipients.', ['app' => 'calendar-appointments']);
foreach ($failed as $fail) {
$this->logger->debug('Failed to deliver email to ' . $fail);
$this->logger->debug('Failed to deliver email to ' . $fail, ['app' => 'calendar-appointments']);
}
throw new ServiceException('Could not send mail for recipient(s) ' . implode(', ', $failed));
}
} catch (Exception $ex) {
$this->logger->error($ex->getMessage(), ['exception' => $ex]);
$this->logger->error($ex->getMessage(), ['exception' => $ex, 'app' => 'calendar-appointments']);
throw new ServiceException('Could not send mail: ' . $ex->getMessage(), $ex->getCode(), $ex);
}
}
@ -320,14 +320,14 @@ class MailService {
try {
$failed = $this->mailer->send($message);
if (count($failed) > 0) {
$this->logger->warning('Mail delivery failed for some recipients.');
$this->logger->warning('Mail delivery failed for some recipients.', ['app' => 'calendar-appointments']);
foreach ($failed as $fail) {
$this->logger->debug('Failed to deliver email to ' . $fail);
$this->logger->debug('Failed to deliver email to ' . $fail, ['app' => 'calendar-appointments']);
}
throw new ServiceException('Could not send mail for recipient(s) ' . implode(', ', $failed));
}
} catch (Exception $ex) {
$this->logger->error('Could not send appointment organizer email: ' . $ex->getMessage(), ['exception' => $ex]);
$this->logger->error('Could not send appointment organizer email: ' . $ex->getMessage(), ['exception' => $ex, 'app' => 'calendar-appointments']);
throw new ServiceException('Could not send mail: ' . $ex->getMessage(), $ex->getCode(), $ex);
}
}

View File

@ -26,17 +26,28 @@ declare(strict_types=1);
namespace OCA\Calendar\Service\Appointments;
use OCA\Calendar\Db\AppointmentConfig;
use Psr\Log\LoggerInterface;
class SlotExtrapolator {
public function __construct(private LoggerInterface $logger) {
}
/**
* @param AppointmentConfig $config
* @param Interval[] $availabilityIntervals
* @param int $to
*
* @return Interval[]
*/
public function extrapolate(AppointmentConfig $config,
array $availabilityIntervals): array {
$this->logger->debug('Intervals before extrapolating:' . count($availabilityIntervals), ['app' => 'calendar-appointments']);
if(empty($availabilityIntervals)) {
return [];
}
foreach ($availabilityIntervals as $availabilityInterval) {
$this->logger->debug('Interval start: ' . $availabilityInterval->getStart() . ', interval end: ' . $availabilityInterval->getEnd(), ['app' => 'calendar-appointments']);
}
$increment = $config->getIncrement();
$length = $config->getLength();
$slots = [];
@ -50,6 +61,7 @@ class SlotExtrapolator {
}
}
$this->logger->debug('Slots after extrapolating:' . count($slots), ['app' => 'calendar-appointments']);
return $slots;
}
}

View File

@ -34,14 +34,12 @@ use OCA\Calendar\Service\Appointments\Interval;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Calendar\ICalendarQuery;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
class AvailabilityGeneratorTest extends TestCase {
/** @var ITimeFactory|MockObject */
private $timeFactory;
/** @var AvailabilityGenerator */
private $generator;
private ITimeFactory|MockObject $timeFactory;
private MockObject|LoggerInterface $logger;
private AvailabilityGenerator $generator;
protected function setUp(): void {
parent::setUp();
@ -50,9 +48,10 @@ class AvailabilityGeneratorTest extends TestCase {
}
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->generator = new AvailabilityGenerator(
$this->timeFactory,
$this->logger,
);
}

View File

@ -32,16 +32,15 @@ use OCA\Calendar\Service\Appointments\Interval;
use OCP\Calendar\ICalendarQuery;
use OCP\Calendar\IManager;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
/**
* @requires OCP\Calendar\ICalendarQuery::newQuery
*/
class DailyLimitFilterTest extends TestCase {
/** @var IManager|MockObject */
private $manager;
/** @var DailyLimitFilter */
private $filter;
private IManager|MockObject $manager;
private MockObject|LoggerInterface $logger;
private DailyLimitFilter $filter;
protected function setUp(): void {
parent::setUp();
@ -51,9 +50,11 @@ class DailyLimitFilterTest extends TestCase {
}
$this->manager = $this->createMock(IManager::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->filter = new DailyLimitFilter(
$this->manager,
$this->logger,
);
}

View File

@ -30,10 +30,13 @@ use OCA\Calendar\Db\AppointmentConfig;
use OCA\Calendar\Service\Appointments\Interval;
use OCA\Calendar\Service\Appointments\SlotExtrapolator;
use OCP\Calendar\ICalendarQuery;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerInterface\PHPUnit\Framework\MockObject\MockObject;
class SlotExtrapolatorTest extends TestCase {
/** @var SlotExtrapolator */
private $extrapolator;
private MockObject|LoggerInterface $logger;
protected function setUp(): void {
parent::setUp();
@ -42,7 +45,8 @@ class SlotExtrapolatorTest extends TestCase {
self::markTestIncomplete();
}
$this->extrapolator = new SlotExtrapolator();
$this->logger = $this->createMock(LoggerInterface::class);
$this->extrapolator = new SlotExtrapolator($this->logger);
}
public function testNoAvailability(): void {