-
Notifications
You must be signed in to change notification settings - Fork 0
Issue 9 configure google calendar integration #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 11 commits
a01d002
108e818
e60a240
cdf4d14
ad19067
3f66aaf
7e72f9d
143a396
05f0bea
9a5edae
9be90f6
9a27950
299df76
950a181
c10105b
4e0a17a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -295,3 +295,6 @@ dist | |
| pyrightconfig.json | ||
|
|
||
| opt/ | ||
|
|
||
| # google api | ||
| server/api/booking/google_calendar/google_calendar_service.json | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -19,4 +19,9 @@ AWS_SECRET_ACCESS_KEY=xxxx | |||
| AWS_STORAGE_BUCKET_NAME=bucket_name | ||||
| AWS_REGION_NAME=ap-southeast-2 | ||||
|
|
||||
| # Google API | ||||
| GOOGLE_CREDENTIALS_FILE=server/api/booking/google_calendar/template_service_account.json | ||||
|
||||
| GOOGLE_CALENDAR_ID=xxxx | ||||
|
|
||||
|
|
||||
|
||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||
| from google.oauth2 import service_account | ||||||||||||||||||||||||
| from googleapiclient.discovery import build | ||||||||||||||||||||||||
| from dotenv import load_dotenv | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Resolve BASE_DIR and load .env | ||||||||||||||||||||||||
| # adjust to server root | ||||||||||||||||||||||||
| BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent | ||||||||||||||||||||||||
| load_dotenv(os.path.join(BASE_DIR, ".env")) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cred_path = os.getenv("GOOGLE_CREDENTIALS_FILE") | ||||||||||||||||||||||||
| calendar_id = os.getenv("GOOGLE_CALENDAR_ID") | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| calendar_id = os.getenv("GOOGLE_CALENDAR_ID") |
Outdated
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Environment variables are being read at module import time (line 13-14), which means they are loaded once when the module is first imported. This can cause issues in testing scenarios or when environment variables change during runtime. Consider moving these reads inside the get_calendar_service() function or making them lazy-loaded.
| cred_path = os.getenv("GOOGLE_CREDENTIALS_FILE") | |
| calendar_id = os.getenv("GOOGLE_CALENDAR_ID") | |
| def get_calendar_service(): | |
| def get_calendar_service(): | |
| cred_path = os.getenv("GOOGLE_CREDENTIALS_FILE") | |
| calendar_id = os.getenv("GOOGLE_CALENDAR_ID") |
Outdated
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using FileNotFoundError is misleading here. The error is raised when the environment variable is not set, not when a file is not found. The file might exist but the environment variable is simply missing. Consider using ValueError or a custom exception instead, as this is a configuration error.
| raise FileNotFoundError( | |
| raise ValueError( |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| import os | ||
| from .client import get_calendar_service | ||
|
|
||
| CALENDAR_ID = os.getenv("GOOGLE_CALENDAR_ID") | ||
|
||
|
|
||
|
|
||
| def create_event(event_data: dict): | ||
|
||
| """ | ||
| Create a new Google Calendar event. | ||
| **Expected event_data keys (Google Calendar API format):** | ||
| - summary (str): Title of the event. | ||
| - description (str, optional): Details about the event. | ||
| - location (str, optional): Physical or virtual location. | ||
| - start (dict): Start date/time. | ||
| Example: | ||
| { | ||
| "dateTime": "2025-02-01T10:00:00+08:00", | ||
| "timeZone": "Australia/Perth" | ||
| } | ||
| - end (dict): End date/time. | ||
| Example: | ||
| { | ||
| "dateTime": "2025-02-01T11:00:00+08:00", | ||
| "timeZone": "Australia/Perth" | ||
| } | ||
| Returns: | ||
| dict: The created event object from Google Calendar. | ||
| """ | ||
| if not CALENDAR_ID: | ||
| raise ValueError( | ||
| "GOOGLE_CALENDAR_ID is missing. Set it in your environment.") | ||
|
||
|
|
||
| service = get_calendar_service() | ||
| return service.events().insert(calendarId=CALENDAR_ID, body=event_data).execute() | ||
|
|
||
|
|
||
| def get_event(event_id: str): | ||
| """ | ||
| Retrieve a specific event. | ||
| Args: | ||
| event_id (str): Google Calendar event ID. | ||
| Returns: | ||
| dict: Event details. | ||
| """ | ||
| if not CALENDAR_ID: | ||
| raise ValueError( | ||
| "GOOGLE_CALENDAR_ID is missing. Set it in your environment.") | ||
| service = get_calendar_service() | ||
| return service.events().get(calendarId=CALENDAR_ID, eventId=event_id).execute() | ||
|
|
||
|
|
||
| def update_event(event_id: str, updated_data: dict): | ||
| """ | ||
| Update an existing event. | ||
| Args: | ||
| event_id (str): Google Calendar event ID. | ||
| updated_data (dict): Same structure as event_data. | ||
| Returns: | ||
| dict: Updated event object. | ||
| """ | ||
| if not CALENDAR_ID: | ||
| raise ValueError( | ||
| "GOOGLE_CALENDAR_ID is missing. Set it in your environment.") | ||
| service = get_calendar_service() | ||
| return service.events().update(calendarId=CALENDAR_ID, eventId=event_id, body=updated_data).execute() | ||
|
|
||
|
|
||
| def delete_event(event_id: str): | ||
| """ | ||
| Delete an event. | ||
| Args: | ||
| event_id (str): Google Calendar event ID. | ||
| Returns: | ||
| dict: Response from Google Calendar API (usually empty). | ||
| """ | ||
| if not CALENDAR_ID: | ||
| raise ValueError( | ||
| "GOOGLE_CALENDAR_ID is missing. Set it in your environment.") | ||
| service = get_calendar_service() | ||
| return service.events().delete(calendarId=CALENDAR_ID, eventId=event_id).execute() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "type": "service_account", | ||
| "project_id": "xxx", | ||
| "private_key_id": "xxx", | ||
| "private_key": "-----BEGIN PRIVATE KEY-----\nxxx\n-----END PRIVATE KEY-----\n", | ||
| "client_email": "[email protected]", | ||
| "client_id": "xxx" | ||
| } |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this test fails when you don't set the environment variables so need to fix it
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import pytest | ||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||
| from api.booking.google_calendar.events import ( | ||||||||||||||||||||||||||||||||||||||||||
| create_event, | ||||||||||||||||||||||||||||||||||||||||||
| get_event, | ||||||||||||||||||||||||||||||||||||||||||
| update_event, | ||||||||||||||||||||||||||||||||||||||||||
| delete_event, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| # skip if no Google Calendar env variables are set | ||||||||||||||||||||||||||||||||||||||||||
| pytestmark = pytest.mark.skipif( | ||||||||||||||||||||||||||||||||||||||||||
| not os.getenv("GOOGLE_CREDENTIALS_FILE") or not os.getenv( | ||||||||||||||||||||||||||||||||||||||||||
| "GOOGLE_CALENDAR_ID"), | ||||||||||||||||||||||||||||||||||||||||||
| reason="Google Calendar integration env vars missing. Set GOOGLE_CREDENTIALS_FILE and GOOGLE_CALENDAR_ID to run this test." | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def test_google_calendar_crud(): | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| event_data = { | ||||||||||||||||||||||||||||||||||||||||||
| "summary": "Test Event", | ||||||||||||||||||||||||||||||||||||||||||
| "description": "CRUD test event", | ||||||||||||||||||||||||||||||||||||||||||
| "start": { | ||||||||||||||||||||||||||||||||||||||||||
| "dateTime": "2025-12-2T15:00:00", | ||||||||||||||||||||||||||||||||||||||||||
| "timeZone": "Australia/Perth", | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| "end": { | ||||||||||||||||||||||||||||||||||||||||||
| "dateTime": "2025-12-2T16:00:00", | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| "dateTime": "2025-12-2T15:00:00", | |
| "timeZone": "Australia/Perth", | |
| }, | |
| "end": { | |
| "dateTime": "2025-12-2T16:00:00", | |
| "dateTime": "2025-12-02T15:00:00", | |
| "timeZone": "Australia/Perth", | |
| }, | |
| "end": { | |
| "dateTime": "2025-12-02T16:00:00", |
Outdated
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The date format in the test is invalid. ISO 8601 format requires two-digit day values. The date "2025-12-2" should be "2025-12-02".
| "dateTime": "2025-12-2T15:00:00", | |
| "timeZone": "Australia/Perth", | |
| }, | |
| "end": { | |
| "dateTime": "2025-12-2T16:00:00", | |
| "dateTime": "2025-12-02T15:00:00", | |
| "timeZone": "Australia/Perth", | |
| }, | |
| "end": { | |
| "dateTime": "2025-12-02T16:00:00", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done