Skip to content

Commit 92dc017

Browse files
authored
Merge pull request #28 from vintasoftware/revert-24-feat/calendar-groups-and-slots-pt4
Revert "feat: add CalendarGroup to Public API (mutations and queries)"
2 parents 24a5745 + 69db88c commit 92dc017

9 files changed

Lines changed: 6 additions & 1005 deletions

File tree

calendar_integration/graphql.py

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
BlockedTimeRecurrenceException,
1111
Calendar,
1212
CalendarEvent,
13-
CalendarEventGroupSelection,
14-
CalendarGroup,
15-
CalendarGroupSlot,
1613
CalendarWebhookEvent,
1714
CalendarWebhookSubscription,
1815
EventAttendance,
@@ -149,10 +146,6 @@ class CalendarEventGraphQLType:
149146
external_attendees: list[ExternalAttendeeGraphQLType] = strawberry_django.field()
150147
resources: list[CalendarGraphQLType] = strawberry_django.field()
151148

152-
# Calendar group linkage (when booked through a CalendarGroup)
153-
calendar_group: "CalendarGroupGraphQLType | None" = strawberry_django.field()
154-
group_selections: list["CalendarEventGroupSelectionGraphQLType"] = strawberry_django.field()
155-
156149
# Bundle representations - events that represent this primary event in child calendars
157150
bundle_representations: list["CalendarEventGraphQLType"] = strawberry_django.field()
158151

@@ -315,65 +308,3 @@ class WebhookSubscriptionStatusGraphQLType:
315308
recent_events_count: int # events in last 24 hours
316309
failed_events_count: int # failed events in last 24 hours
317310
success_rate: float # percentage of successful events in last 24 hours
318-
319-
320-
# ---------------------------------------------------------------------------
321-
# CalendarGroup types
322-
# ---------------------------------------------------------------------------
323-
@strawberry_django.type(CalendarGroupSlot)
324-
class CalendarGroupSlotGraphQLType:
325-
id: strawberry.auto # noqa: A003
326-
name: strawberry.auto
327-
description: strawberry.auto
328-
order: strawberry.auto
329-
required_count: strawberry.auto
330-
created: datetime.datetime
331-
modified: datetime.datetime
332-
333-
calendars: list[CalendarGraphQLType] = strawberry_django.field()
334-
335-
336-
@strawberry_django.type(CalendarGroup)
337-
class CalendarGroupGraphQLType:
338-
id: strawberry.auto # noqa: A003
339-
name: strawberry.auto
340-
description: strawberry.auto
341-
created: datetime.datetime
342-
modified: datetime.datetime
343-
344-
slots: list[CalendarGroupSlotGraphQLType] = strawberry_django.field()
345-
346-
347-
@strawberry_django.type(CalendarEventGroupSelection)
348-
class CalendarEventGroupSelectionGraphQLType:
349-
id: strawberry.auto # noqa: A003
350-
created: datetime.datetime
351-
modified: datetime.datetime
352-
353-
slot: CalendarGroupSlotGraphQLType = strawberry_django.field()
354-
calendar: CalendarGraphQLType = strawberry_django.field()
355-
356-
357-
@strawberry.type
358-
class CalendarGroupSlotAvailabilityGraphQLType:
359-
"""How many of a slot's pool calendars are available for a given range."""
360-
361-
slot_id: int
362-
available_calendar_ids: list[int]
363-
364-
365-
@strawberry.type
366-
class CalendarGroupRangeAvailabilityGraphQLType:
367-
"""Per-slot availability for a single range."""
368-
369-
start_time: datetime.datetime
370-
end_time: datetime.datetime
371-
slots: list[CalendarGroupSlotAvailabilityGraphQLType]
372-
373-
374-
@strawberry.type
375-
class BookableSlotProposalGraphQLType:
376-
"""A concrete time window where every slot in a group is satisfied."""
377-
378-
start_time: datetime.datetime
379-
end_time: datetime.datetime

calendar_integration/mutations.py

Lines changed: 1 addition & 274 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,18 @@
11
"""GraphQL mutations for calendar integration webhook management."""
22

3-
import datetime
43
from dataclasses import dataclass
54
from typing import TYPE_CHECKING, Annotated, cast
65

76
import strawberry
87
from dependency_injector.wiring import Provide, inject
98
from graphql import GraphQLError
109

11-
from calendar_integration.exceptions import CalendarGroupError
12-
from calendar_integration.graphql import (
13-
CalendarEventGraphQLType,
14-
CalendarGroupGraphQLType,
15-
CalendarWebhookSubscriptionGraphQLType,
16-
)
17-
from calendar_integration.models import CalendarGroup
18-
from calendar_integration.services.dataclasses import (
19-
CalendarGroupEventInputData,
20-
CalendarGroupInputData,
21-
CalendarGroupSlotInputData,
22-
CalendarGroupSlotSelectionInputData,
23-
EventAttendanceInputData,
24-
EventExternalAttendanceInputData,
25-
ExternalAttendeeInputData,
26-
)
10+
from calendar_integration.graphql import CalendarWebhookSubscriptionGraphQLType
2711
from calendar_integration.services.webhook_analytics_service import WebhookAnalyticsService
2812
from organizations.models import Organization
2913

3014

3115
if TYPE_CHECKING:
32-
from calendar_integration.services.calendar_group_service import CalendarGroupService
3316
from calendar_integration.services.calendar_service import CalendarService
3417

3518

@@ -237,259 +220,3 @@ def cleanup_webhook_events(
237220
deleted_count=0,
238221
error_message=f"Failed to cleanup events: {e!s}",
239222
)
240-
241-
242-
# ---------------------------------------------------------------------------
243-
# CalendarGroup mutations
244-
# ---------------------------------------------------------------------------
245-
246-
247-
@dataclass
248-
class CalendarGroupMutationDependencies:
249-
"""Dependencies for CalendarGroup mutations."""
250-
251-
calendar_group_service: "CalendarGroupService"
252-
calendar_service: "CalendarService"
253-
254-
255-
@inject
256-
def get_calendar_group_mutation_dependencies(
257-
calendar_group_service: Annotated[
258-
"CalendarGroupService | None", Provide["calendar_group_service"]
259-
] = None,
260-
calendar_service: Annotated["CalendarService | None", Provide["calendar_service"]] = None,
261-
) -> CalendarGroupMutationDependencies:
262-
required = [calendar_group_service, calendar_service]
263-
if any(dep is None for dep in required):
264-
raise GraphQLError(
265-
f"Missing required dependency {', '.join([str(d) for d in required if d is None])}"
266-
)
267-
return CalendarGroupMutationDependencies(
268-
calendar_group_service=cast("CalendarGroupService", calendar_group_service),
269-
calendar_service=cast("CalendarService", calendar_service),
270-
)
271-
272-
273-
@strawberry.input
274-
class CalendarGroupSlotInput:
275-
name: str
276-
calendar_ids: list[int]
277-
required_count: int = 1
278-
description: str = ""
279-
order: int = 0
280-
281-
282-
@strawberry.input
283-
class CalendarGroupInput:
284-
organization_id: int
285-
name: str
286-
description: str = ""
287-
slots: list[CalendarGroupSlotInput] = strawberry.field(default_factory=list)
288-
289-
290-
@strawberry.input
291-
class UpdateCalendarGroupInput:
292-
organization_id: int
293-
group_id: int
294-
name: str
295-
description: str = ""
296-
slots: list[CalendarGroupSlotInput] = strawberry.field(default_factory=list)
297-
298-
299-
@strawberry.input
300-
class DeleteCalendarGroupInput:
301-
organization_id: int
302-
group_id: int
303-
304-
305-
@strawberry.input
306-
class CalendarGroupSlotSelectionInput:
307-
slot_id: int
308-
calendar_ids: list[int]
309-
310-
311-
@strawberry.input
312-
class ExternalAttendeeInput:
313-
email: str
314-
name: str = ""
315-
id: int | None = None # noqa: A003
316-
317-
318-
@strawberry.input
319-
class EventExternalAttendanceInput:
320-
external_attendee: ExternalAttendeeInput
321-
322-
323-
@strawberry.input
324-
class EventAttendanceInput:
325-
user_id: int
326-
327-
328-
@strawberry.input
329-
class CalendarGroupEventInput:
330-
organization_id: int
331-
group_id: int
332-
title: str
333-
description: str
334-
start_time: datetime.datetime
335-
end_time: datetime.datetime
336-
timezone: str
337-
slot_selections: list[CalendarGroupSlotSelectionInput]
338-
attendances: list[EventAttendanceInput] = strawberry.field(default_factory=list)
339-
external_attendances: list[EventExternalAttendanceInput] = strawberry.field(
340-
default_factory=list
341-
)
342-
343-
344-
@strawberry.type
345-
class CalendarGroupResult:
346-
success: bool
347-
group: CalendarGroupGraphQLType | None = None
348-
error_message: str | None = None
349-
350-
351-
@strawberry.type
352-
class DeleteCalendarGroupResult:
353-
success: bool
354-
error_message: str | None = None
355-
356-
357-
@strawberry.type
358-
class CalendarGroupEventResult:
359-
success: bool
360-
event: CalendarEventGraphQLType | None = None
361-
error_message: str | None = None
362-
363-
364-
def _to_slot_input_data(slots: list[CalendarGroupSlotInput]) -> list[CalendarGroupSlotInputData]:
365-
return [
366-
CalendarGroupSlotInputData(
367-
name=s.name,
368-
calendar_ids=list(s.calendar_ids),
369-
required_count=s.required_count,
370-
description=s.description,
371-
order=s.order,
372-
)
373-
for s in slots
374-
]
375-
376-
377-
def _load_organization(organization_id: int) -> Organization | None:
378-
try:
379-
return Organization.objects.get(id=organization_id)
380-
except Organization.DoesNotExist:
381-
return None
382-
383-
384-
@strawberry.type
385-
class CalendarGroupMutations:
386-
"""GraphQL mutations for CalendarGroup CRUD and grouped event booking."""
387-
388-
@strawberry.mutation
389-
def create_calendar_group(
390-
self,
391-
input: CalendarGroupInput, # noqa: A002
392-
) -> CalendarGroupResult:
393-
organization = _load_organization(input.organization_id)
394-
if organization is None:
395-
return CalendarGroupResult(success=False, error_message="Organization not found")
396-
deps = get_calendar_group_mutation_dependencies()
397-
deps.calendar_group_service.initialize(organization=organization)
398-
try:
399-
group = deps.calendar_group_service.create_group(
400-
CalendarGroupInputData(
401-
name=input.name,
402-
description=input.description,
403-
slots=_to_slot_input_data(input.slots),
404-
)
405-
)
406-
except CalendarGroupError as e:
407-
return CalendarGroupResult(success=False, error_message=str(e))
408-
return CalendarGroupResult(success=True, group=group) # type: ignore[arg-type]
409-
410-
@strawberry.mutation
411-
def update_calendar_group(
412-
self,
413-
input: UpdateCalendarGroupInput, # noqa: A002
414-
) -> CalendarGroupResult:
415-
organization = _load_organization(input.organization_id)
416-
if organization is None:
417-
return CalendarGroupResult(success=False, error_message="Organization not found")
418-
deps = get_calendar_group_mutation_dependencies()
419-
deps.calendar_group_service.initialize(organization=organization)
420-
try:
421-
group = deps.calendar_group_service.update_group(
422-
group_id=input.group_id,
423-
data=CalendarGroupInputData(
424-
name=input.name,
425-
description=input.description,
426-
slots=_to_slot_input_data(input.slots),
427-
),
428-
)
429-
except CalendarGroup.DoesNotExist:
430-
return CalendarGroupResult(success=False, error_message="Group not found")
431-
except CalendarGroupError as e:
432-
return CalendarGroupResult(success=False, error_message=str(e))
433-
return CalendarGroupResult(success=True, group=group) # type: ignore[arg-type]
434-
435-
@strawberry.mutation
436-
def delete_calendar_group(
437-
self,
438-
input: DeleteCalendarGroupInput, # noqa: A002
439-
) -> DeleteCalendarGroupResult:
440-
organization = _load_organization(input.organization_id)
441-
if organization is None:
442-
return DeleteCalendarGroupResult(success=False, error_message="Organization not found")
443-
deps = get_calendar_group_mutation_dependencies()
444-
deps.calendar_group_service.initialize(organization=organization)
445-
try:
446-
deps.calendar_group_service.delete_group(group_id=input.group_id)
447-
except CalendarGroup.DoesNotExist:
448-
return DeleteCalendarGroupResult(success=False, error_message="Group not found")
449-
except CalendarGroupError as e:
450-
return DeleteCalendarGroupResult(success=False, error_message=str(e))
451-
return DeleteCalendarGroupResult(success=True)
452-
453-
@strawberry.mutation
454-
def create_calendar_group_event(
455-
self,
456-
input: CalendarGroupEventInput, # noqa: A002
457-
) -> CalendarGroupEventResult:
458-
organization = _load_organization(input.organization_id)
459-
if organization is None:
460-
return CalendarGroupEventResult(success=False, error_message="Organization not found")
461-
deps = get_calendar_group_mutation_dependencies()
462-
deps.calendar_service.initialize_without_provider(organization=organization)
463-
deps.calendar_group_service.initialize(organization=organization)
464-
data = CalendarGroupEventInputData(
465-
title=input.title,
466-
description=input.description,
467-
start_time=input.start_time,
468-
end_time=input.end_time,
469-
timezone=input.timezone,
470-
group_id=input.group_id,
471-
slot_selections=[
472-
CalendarGroupSlotSelectionInputData(
473-
slot_id=s.slot_id, calendar_ids=list(s.calendar_ids)
474-
)
475-
for s in input.slot_selections
476-
],
477-
attendances=[EventAttendanceInputData(user_id=a.user_id) for a in input.attendances],
478-
external_attendances=[
479-
EventExternalAttendanceInputData(
480-
external_attendee=ExternalAttendeeInputData(
481-
email=e.external_attendee.email,
482-
name=e.external_attendee.name,
483-
id=e.external_attendee.id,
484-
)
485-
)
486-
for e in input.external_attendances
487-
],
488-
)
489-
try:
490-
event = deps.calendar_group_service.create_grouped_event(data)
491-
except CalendarGroup.DoesNotExist:
492-
return CalendarGroupEventResult(success=False, error_message="Group not found")
493-
except CalendarGroupError as e:
494-
return CalendarGroupEventResult(success=False, error_message=str(e))
495-
return CalendarGroupEventResult(success=True, event=event) # type: ignore[arg-type]

0 commit comments

Comments
 (0)