-
Notifications
You must be signed in to change notification settings - Fork 0
jsp working updates #83
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 all commits
a7460d9
3435649
804f2c1
c0f7857
6a32713
48967ec
3e8ce1f
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 | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||||||||||||||||||||
| import { EnvironmentManager } from "./environment"; | ||||||||||||||||||||||||||||||||
| import type { isProcessed, ProcessedEvents, UserSettings } from "./types"; | ||||||||||||||||||||||||||||||||
| import type { isProcessed, ProcessedEvents, UniversityCalendarEvent, UniversityEventCategoryWithCount, UserSettings } from "./types"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export class API { | ||||||||||||||||||||||||||||||||
| private static async getBaseUrl(): Promise<string> { | ||||||||||||||||||||||||||||||||
|
|
@@ -111,4 +111,199 @@ export class API { | |||||||||||||||||||||||||||||||
| return response.json(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public static async getUniversityEventCategories(): Promise<{ categories: UniversityEventCategoryWithCount[] }> { | ||||||||||||||||||||||||||||||||
| const baseUrl = await this.getBaseUrl(); | ||||||||||||||||||||||||||||||||
| const token = await this.getJwtToken(); | ||||||||||||||||||||||||||||||||
| const response = await fetch(`${baseUrl}/university_calendar_events/categories`, { | ||||||||||||||||||||||||||||||||
| method: 'GET', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${token}` | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| return response.json(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public static async getUniversityEvents(params?: { category?: string; categories?: string; start_date?: string; end_date?: string; term_id?: string; page?: number; per_page?: number }): Promise<{ events: UniversityCalendarEvent[]; meta: { current_page: number; total_pages: number; total_count: number; per_page: number } }> { | ||||||||||||||||||||||||||||||||
| const baseUrl = await this.getBaseUrl(); | ||||||||||||||||||||||||||||||||
| const token = await this.getJwtToken(); | ||||||||||||||||||||||||||||||||
| const searchParams = new URLSearchParams(); | ||||||||||||||||||||||||||||||||
| if (params?.category) searchParams.append('category', params.category); | ||||||||||||||||||||||||||||||||
| if (params?.categories) searchParams.append('categories', params.categories); | ||||||||||||||||||||||||||||||||
| if (params?.start_date) searchParams.append('start_date', params.start_date); | ||||||||||||||||||||||||||||||||
| if (params?.end_date) searchParams.append('end_date', params.end_date); | ||||||||||||||||||||||||||||||||
| if (params?.term_id) searchParams.append('term_id', params.term_id); | ||||||||||||||||||||||||||||||||
| if (params?.page) searchParams.append('page', params.page.toString()); | ||||||||||||||||||||||||||||||||
| if (params?.per_page) searchParams.append('per_page', params.per_page.toString()); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const url = `${baseUrl}/university_calendar_events${searchParams.toString() ? '?' + searchParams.toString() : ''}`; | ||||||||||||||||||||||||||||||||
| const response = await fetch(url, { | ||||||||||||||||||||||||||||||||
| method: 'GET', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${token}` | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| return response.json(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public static async getHolidays(params?: { term_id?: string; start_date?: string; end_date?: string }): Promise<{ holidays: UniversityCalendarEvent[] }> { | ||||||||||||||||||||||||||||||||
| const baseUrl = await this.getBaseUrl(); | ||||||||||||||||||||||||||||||||
| const token = await this.getJwtToken(); | ||||||||||||||||||||||||||||||||
| const searchParams = new URLSearchParams(); | ||||||||||||||||||||||||||||||||
| if (params?.term_id) searchParams.append('term_id', params.term_id); | ||||||||||||||||||||||||||||||||
| if (params?.start_date) searchParams.append('start_date', params.start_date); | ||||||||||||||||||||||||||||||||
| if (params?.end_date) searchParams.append('end_date', params.end_date); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const url = `${baseUrl}/university_calendar_events/holidays${searchParams.toString() ? '?' + searchParams.toString() : ''}`; | ||||||||||||||||||||||||||||||||
| const response = await fetch(url, { | ||||||||||||||||||||||||||||||||
| method: 'GET', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${token}` | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| return response.json(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Course processing endpoints | ||||||||||||||||||||||||||||||||
| public static async processCourses(courses: any[]): Promise<{ user_pub: string; ics_url: string }> { | ||||||||||||||||||||||||||||||||
| const baseUrl = await this.getBaseUrl(); | ||||||||||||||||||||||||||||||||
| const token = await this.getJwtToken(); | ||||||||||||||||||||||||||||||||
| const response = await fetch(`${baseUrl}/process_courses`, { | ||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${token}`, | ||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json' | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| body: JSON.stringify(courses) | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| return response.json(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public static async reprocessCourses(courses: any[]): Promise<{ | ||||||||||||||||||||||||||||||||
| ics_url: string; | ||||||||||||||||||||||||||||||||
| removed_enrollments: number; | ||||||||||||||||||||||||||||||||
| removed_courses: Array<{ crn: number; title: string; course_number: number }>; | ||||||||||||||||||||||||||||||||
| processed_courses: any[]; | ||||||||||||||||||||||||||||||||
| }> { | ||||||||||||||||||||||||||||||||
| const baseUrl = await this.getBaseUrl(); | ||||||||||||||||||||||||||||||||
| const token = await this.getJwtToken(); | ||||||||||||||||||||||||||||||||
| const response = await fetch(`${baseUrl}/courses/reprocess`, { | ||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${token}`, | ||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json' | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| body: JSON.stringify({ courses }) | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| }); | |
| }); | |
| if (!response.ok) { | |
| const errorText = await response.text().catch(() => ''); | |
| throw new Error( | |
| `Failed to reprocess courses: ${response.status} ${response.statusText}` + | |
| (errorText ? ` - ${errorText}` : '') | |
| ); | |
| } |
Copilot
AI
Dec 24, 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 getMeetingTimePreference method doesn't check the response status. If the request fails (e.g., 404 for non-existent meeting time), calling .json() on the error response could produce unexpected results or throw an error that isn't properly handled.
| }); | |
| }); | |
| if (!response.ok) { | |
| let errorBody: string | undefined; | |
| try { | |
| errorBody = await response.text(); | |
| } catch { | |
| // Ignore body parsing errors; we'll fall back to status text. | |
| } | |
| const message = `Failed to fetch meeting time preference (status ${response.status}): ` + | |
| (errorBody && errorBody.trim().length > 0 ? errorBody : response.statusText); | |
| throw new Error(message); | |
| } |
Copilot
AI
Dec 24, 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 updateMeetingTimePreference method doesn't validate the response status. If the API returns an error (e.g., 401, 403, or 500), the method will still attempt to parse JSON and return it, which could lead to unexpected behavior in the calling code.
| }); | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`Failed to update meeting time preference: ${response.status} ${response.statusText}`); | |
| } |
Copilot
AI
Dec 24, 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 disconnectAccount API method doesn't check the response status or handle potential errors from the fetch call. If the DELETE request fails (network error, 404, 500, etc.), the promise will silently resolve without throwing, and the calling code won't know the disconnection failed.
| await fetch(`${baseUrl}/user/oauth_credentials/${credentialId}`, { | |
| method: 'DELETE', | |
| headers: { | |
| 'Authorization': `Bearer ${token}` | |
| } | |
| }); | |
| const response = await fetch(`${baseUrl}/user/oauth_credentials/${credentialId}`, { | |
| method: 'DELETE', | |
| headers: { | |
| 'Authorization': `Bearer ${token}` | |
| } | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`Failed to disconnect account (status ${response.status} ${response.statusText})`); | |
| } |
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
processCoursesmethod doesn't check if the response is successful before attempting to parse JSON. If the fetch fails with a non-2xx status code, calling.json()could throw an error or return unexpected data. Consider adding response status validation similar to the pattern used in the old inline fetch code.