Skip to content

Commit 3e3430c

Browse files
authored
[ADVAPP-2177]: Some users who have a large number of calendar entries timeout when attempting to sync their institutional calendar using the calendar integration feature (#2117)
* [ADVAPP-2177]: Some users who have a large number of calendar entries timeout when attempting to sync their institutional calendar using the calendar integration feature * Update code formatting and copyright headers
1 parent 02f8cd7 commit 3e3430c

6 files changed

Lines changed: 150 additions & 19 deletions

File tree

app-modules/meeting-center/src/Console/Commands/SyncEvents.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
namespace AdvisingApp\MeetingCenter\Console\Commands;
3838

39-
use AdvisingApp\MeetingCenter\Managers\CalendarManager;
39+
use AdvisingApp\MeetingCenter\Jobs\SyncCalendar;
4040
use App\Models\User;
4141
use Illuminate\Console\Command;
4242
use Spatie\Multitenancy\Commands\Concerns\TenantAware;
@@ -53,9 +53,7 @@ public function handle(): int
5353
{
5454
$user = User::where('email', config('local_development.super_admin.email'))->first();
5555

56-
resolve(CalendarManager::class)
57-
->driver($user->calendar->provider_type->value)
58-
->syncEvents($user->calendar);
56+
dispatch(new SyncCalendar($user->calendar));
5957

6058
return static::SUCCESS;
6159
}

app-modules/meeting-center/src/Filament/Resources/CalendarEvents/Pages/ListCalendarEvents.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
namespace AdvisingApp\MeetingCenter\Filament\Resources\CalendarEvents\Pages;
3838

3939
use AdvisingApp\MeetingCenter\Filament\Resources\CalendarEvents\CalendarEventResource;
40+
use AdvisingApp\MeetingCenter\Jobs\SyncCalendar;
4041
use AdvisingApp\MeetingCenter\Managers\CalendarManager;
4142
use AdvisingApp\MeetingCenter\Managers\Contracts\CalendarInterface;
4243
use App\Filament\Tables\Columns\IdColumn;
@@ -50,8 +51,10 @@
5051
use Filament\Actions\ViewAction;
5152
use Filament\Facades\Filament;
5253
use Filament\Forms\Components\Select;
54+
use Filament\Notifications\Notification;
5355
use Filament\Resources\Pages\ListRecords;
5456
use Filament\Tables\Columns\TextColumn;
57+
use Filament\Tables\Enums\PaginationMode;
5558
use Filament\Tables\Filters\Filter;
5659
use Filament\Tables\Table;
5760
use Illuminate\Database\Eloquent\Builder;
@@ -121,10 +124,7 @@ public function selectCalendarAction(): Action
121124

122125
$calendar->saveQuietly();
123126

124-
//TODO: queue
125-
resolve(CalendarManager::class)
126-
->driver($calendar->provider_type->value)
127-
->syncEvents($calendar);
127+
dispatch(new SyncCalendar($calendar));
128128
});
129129
}
130130

@@ -170,7 +170,9 @@ public function table(Table $table): Table
170170

171171
return $query->whereRelation('calendar', 'user_id', $user->id);
172172
})
173-
->defaultSort('starts_at');
173+
->defaultSort('starts_at')
174+
->paginationMode(PaginationMode::Cursor)
175+
->poll('10s');
174176
}
175177

176178
protected function getHeaderActions(): array
@@ -182,11 +184,13 @@ protected function getHeaderActions(): array
182184
/** @var User $user */
183185
$user = auth()->user();
184186

185-
resolve(CalendarManager::class)
186-
->driver($user->calendar->provider_type->value)
187-
->syncEvents($user->calendar);
187+
dispatch(new SyncCalendar($user->calendar));
188188

189-
$this->dispatch('refresh-events');
189+
Notification::make()
190+
->title('Calendar synchronization started')
191+
->body('Your calendar is being synchronized in the background. This may take a few moments.')
192+
->success()
193+
->send();
190194
}),
191195
];
192196
}

app-modules/meeting-center/src/Jobs/SyncCalendar.php

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@
3636

3737
namespace AdvisingApp\MeetingCenter\Jobs;
3838

39-
use AdvisingApp\MeetingCenter\Managers\CalendarManager;
4039
use AdvisingApp\MeetingCenter\Models\Calendar;
4140
use App\Models\Tenant;
41+
use Carbon\Carbon;
4242
use Illuminate\Bus\Queueable;
4343
use Illuminate\Contracts\Queue\ShouldBeUnique;
4444
use Illuminate\Contracts\Queue\ShouldQueue;
4545
use Illuminate\Foundation\Bus\Dispatchable;
4646
use Illuminate\Queue\InteractsWithQueue;
4747
use Illuminate\Queue\SerializesModels;
48+
use Illuminate\Support\Facades\Bus;
4849

4950
class SyncCalendar implements ShouldQueue, ShouldBeUnique
5051
{
@@ -65,8 +66,41 @@ public function uniqueId(): string
6566

6667
public function handle(): void
6768
{
68-
resolve(CalendarManager::class)
69-
->driver($this->calendar->provider_type->value)
70-
->syncEvents($this->calendar);
69+
$now = Carbon::now();
70+
$jobs = [];
71+
72+
// Chunk 1: Current month + next month
73+
$jobs[] = new SyncCalendarPeriod(
74+
$this->calendar,
75+
$now->copy()->startOfMonth(),
76+
$now->copy()->addMonth()->endOfMonth()
77+
);
78+
79+
// Chunk 2: Past 2 months
80+
$jobs[] = new SyncCalendarPeriod(
81+
$this->calendar,
82+
$now->copy()->subMonths(2)->startOfMonth(),
83+
$now->copy()->subMonth()->endOfMonth()
84+
);
85+
86+
// Chunks 3-7: Remaining future months for the next year
87+
for ($month = 2; $month <= 10; $month += 2) {
88+
$jobs[] = new SyncCalendarPeriod(
89+
$this->calendar,
90+
$now->copy()->addMonths($month)->startOfMonth(),
91+
$now->copy()->addMonths($month + 1)->endOfMonth()
92+
);
93+
}
94+
95+
// Chunks 8-12: Remaining past months for the last year
96+
for ($month = 3; $month <= 11; $month += 2) {
97+
$jobs[] = new SyncCalendarPeriod(
98+
$this->calendar,
99+
$now->copy()->subMonths($month + 1)->startOfMonth(),
100+
$now->copy()->subMonths($month)->endOfMonth()
101+
);
102+
}
103+
104+
Bus::chain($jobs)->dispatch();
71105
}
72106
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
/*
4+
<COPYRIGHT>
5+
6+
Copyright © 2016-2025, Canyon GBS LLC. All rights reserved.
7+
8+
Advising App™ is licensed under the Elastic License 2.0. For more details,
9+
see https://github.com/canyongbs/advisingapp/blob/main/LICENSE.
10+
11+
Notice:
12+
13+
- You may not provide the software to third parties as a hosted or managed
14+
service, where the service provides users with access to any substantial set of
15+
the features or functionality of the software.
16+
- You may not move, change, disable, or circumvent the license key functionality
17+
in the software, and you may not remove or obscure any functionality in the
18+
software that is protected by the license key.
19+
- You may not alter, remove, or obscure any licensing, copyright, or other notices
20+
of the licensor in the software. Any use of the licensor’s trademarks is subject
21+
to applicable law.
22+
- Canyon GBS LLC respects the intellectual property rights of others and expects the
23+
same in return. Canyon GBS™ and Advising App™ are registered trademarks of
24+
Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks
25+
vigorously.
26+
- The software solution, including services, infrastructure, and code, is offered as a
27+
Software as a Service (SaaS) by Canyon GBS LLC.
28+
- Use of this software implies agreement to the license terms and conditions as stated
29+
in the Elastic License 2.0.
30+
31+
For more information or inquiries please visit our website at
32+
https://www.canyongbs.com or contact us via email at legal@canyongbs.com.
33+
34+
</COPYRIGHT>
35+
*/
36+
37+
namespace AdvisingApp\MeetingCenter\Jobs;
38+
39+
use AdvisingApp\MeetingCenter\Managers\CalendarManager;
40+
use AdvisingApp\MeetingCenter\Models\Calendar;
41+
use App\Models\Tenant;
42+
use Carbon\Carbon;
43+
use DateTime;
44+
use Illuminate\Bus\Queueable;
45+
use Illuminate\Contracts\Queue\ShouldBeUnique;
46+
use Illuminate\Contracts\Queue\ShouldQueue;
47+
use Illuminate\Foundation\Bus\Dispatchable;
48+
use Illuminate\Queue\InteractsWithQueue;
49+
use Illuminate\Queue\SerializesModels;
50+
51+
class SyncCalendarPeriod implements ShouldQueue, ShouldBeUnique
52+
{
53+
use Dispatchable;
54+
use InteractsWithQueue;
55+
use Queueable;
56+
use SerializesModels;
57+
58+
public function __construct(
59+
protected Calendar $calendar,
60+
protected Carbon $start,
61+
protected Carbon $end,
62+
) {
63+
$this->onQueue(config('meeting-center.queue'));
64+
}
65+
66+
public function uniqueId(): string
67+
{
68+
return Tenant::current()->getKey() . ':' . $this->calendar->getKey() . ':' . $this->start->format('Y-m-d') . ':' . $this->end->format('Y-m-d');
69+
}
70+
71+
public function handle(): void
72+
{
73+
resolve(CalendarManager::class)
74+
->driver($this->calendar->provider_type->value)
75+
->syncEvents(
76+
$this->calendar,
77+
new DateTime($this->start->toDateTimeString()),
78+
new DateTime($this->end->toDateTimeString())
79+
);
80+
}
81+
}

app-modules/meeting-center/src/Managers/GoogleCalendarManager.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ public function deleteEvent(CalendarEvent $event): void
158158

159159
public function syncEvents(Calendar $calendar, ?DateTime $start = null, ?DateTime $end = null, ?int $perPage = null): void
160160
{
161+
$start = $start ?? now()->subYear()->startOfDay();
162+
$end = $end ?? now()->addYear()->endOfDay();
163+
161164
$events = collect($this->getEvents($calendar, $start, $end, $perPage));
162165

163166
$events
@@ -195,8 +198,13 @@ function (Event $event) use ($calendar) {
195198
->whereNull('provider_id')
196199
->each(fn ($event) => $this->createEvent($event));
197200

198-
// TODO: needs to only delete orphaned events and not previous events
199-
// $calendar->events()->whereNotIn('provider_id', $events->pluck('id'))->delete();
201+
// Only delete orphaned events within the synced date range
202+
$calendar->events()
203+
->whereNotNull('provider_id')
204+
->where('starts_at', '>=', $start)
205+
->where('starts_at', '<', $end)
206+
->whereNotIn('provider_id', $events->pluck('id'))
207+
->delete();
200208
}
201209

202210
public function revokeToken(Calendar $calendar): bool

app-modules/meeting-center/src/Managers/OutlookCalendarManager.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ public function deleteEvent(CalendarEvent $event): void
203203

204204
public function syncEvents(Calendar $calendar, ?DateTime $start = null, ?DateTime $end = null, ?int $perPage = null): void
205205
{
206+
$start = $start ?? now()->subYear()->startOfDay();
207+
$end = $end ?? now()->addYear()->endOfDay();
208+
206209
$providerEvents = collect($this->getEvents($calendar, $start, $end, $perPage));
207210

208211
$providerEvents
@@ -229,8 +232,11 @@ public function syncEvents(Calendar $calendar, ?DateTime $start = null, ?DateTim
229232
->whereNull('provider_id')
230233
->each(fn ($event) => $this->createEvent($event));
231234

235+
// Only delete orphaned events within the synced date range
232236
$calendar->events()
233237
->whereNotNull('provider_id')
238+
->where('starts_at', '>=', $start)
239+
->where('starts_at', '<', $end)
234240
->whereNotIn('provider_id', $providerEvents->map(fn (Event $event) => $event->getId()))
235241
->delete();
236242
}

0 commit comments

Comments
 (0)