|
1 |
| -import axios from "axios"; |
| 1 | +import axios, { AxiosRequestConfig } from "axios"; |
2 | 2 | import { getCanvasID, getCanvasToken } from "./supabase";
|
3 |
| -import { |
4 |
| - MissingAssignmentResponse, |
5 |
| - AnnouncementPost, |
6 |
| - Course, |
7 |
| - Assignment, |
8 |
| -} from "../types"; |
| 3 | +import { AnnouncementPost, Course, Assignment } from "../types"; |
9 | 4 | import { CONFIG } from "../config";
|
10 | 5 |
|
11 | 6 | export async function fetchData<T>(
|
12 |
| - url: string, |
13 |
| - token: string, |
14 |
| - params: Record<string, unknown> = {}, |
| 7 | + userId: string, |
| 8 | + endpoint: string, |
| 9 | + params: Record<string, string | number | boolean | undefined> = {}, |
| 10 | + config: AxiosRequestConfig = {}, |
15 | 11 | ): Promise<T> {
|
16 | 12 | try {
|
17 |
| - const response = await axios.get<T>(url, { |
| 13 | + const canvasToken = await getCanvasToken(userId); |
| 14 | + if (!canvasToken) { |
| 15 | + throw new Error("Canvas token is not found."); |
| 16 | + } |
| 17 | + const response = await axios.get<T>(`${CONFIG.CANVAS_DOMAIN}${endpoint}`, { |
| 18 | + timeout: 10000, |
18 | 19 | headers: {
|
19 |
| - Authorization: `Bearer ${token}`, |
| 20 | + Authorization: `Bearer ${canvasToken}`, |
20 | 21 | "Content-Type": "application/json",
|
21 | 22 | },
|
22 | 23 | params,
|
| 24 | + ...config, |
23 | 25 | });
|
24 | 26 | return response.data;
|
25 | 27 | } catch (error) {
|
26 | 28 | if (axios.isAxiosError(error)) {
|
27 |
| - console.error(`Axios error fetching data from ${url}:`, error.message); |
| 29 | + console.error( |
| 30 | + `Axios error fetching data from ${endpoint}:`, |
| 31 | + error.message, |
| 32 | + ); |
28 | 33 | } else {
|
29 |
| - console.error(`Unexpected error fetching data from ${url}:`, error); |
| 34 | + console.error(`Unexpected error fetching data from ${endpoint}:`, error); |
30 | 35 | }
|
31 | 36 | throw error;
|
32 | 37 | }
|
33 | 38 | }
|
34 | 39 |
|
35 |
| -export async function fetchCourses( |
36 |
| - userId: string, |
37 |
| -): Promise<{ message: string; courses: Course[] }> { |
38 |
| - const canvasToken = await getCanvasToken(userId); |
39 |
| - if (!canvasToken) { |
40 |
| - return { |
41 |
| - message: |
42 |
| - "You are not enrolled in any courses; Please enter your token with the command /account", |
43 |
| - courses: [], |
44 |
| - }; |
45 |
| - } |
| 40 | +export async function fetchCourses(userId: string) { |
46 | 41 | try {
|
47 |
| - const response = await axios.get<{ courses: Course[] }>( |
48 |
| - `${CONFIG.CANVAS_DOMAIN}courses`, |
| 42 | + const canvasID = await getCanvasID(userId); |
| 43 | + const courses = await fetchData<Course[]>( |
| 44 | + userId, |
| 45 | + `users/${canvasID}/courses`, |
49 | 46 | {
|
50 |
| - headers: { |
51 |
| - Authorization: `Bearer ${canvasToken}`, |
52 |
| - "Content-Type": "application/json", |
53 |
| - }, |
54 |
| - params: { |
55 |
| - enrollment_type: "student", |
56 |
| - enrollment_state: "active", |
57 |
| - user_id: userId, |
58 |
| - }, |
| 47 | + enrollment_type: "student", |
| 48 | + enrollment_state: "active", |
59 | 49 | },
|
60 | 50 | );
|
61 |
| - return { |
62 |
| - message: "Courses fetched successfully", |
63 |
| - courses: response.data.courses || [], |
64 |
| - }; |
| 51 | + return courses; |
65 | 52 | } catch (error) {
|
66 |
| - console.error("Error fetching courses:", error); |
67 |
| - return { |
68 |
| - message: "An error occurred while fetching courses.", |
69 |
| - courses: [], |
70 |
| - }; |
| 53 | + console.error("Failed to fetch courses:", error); |
| 54 | + throw new Error("Error fetching courses."); |
71 | 55 | }
|
72 | 56 | }
|
73 | 57 |
|
74 | 58 | export async function fetchAssignments(
|
75 | 59 | courseId: number,
|
76 | 60 | userId: string,
|
77 |
| -): Promise<Assignment[]> { |
78 |
| - const canvasToken = await getCanvasToken(userId); |
| 61 | +): Promise<{ message: string; assignments: Assignment[] }> { |
79 | 62 | try {
|
80 |
| - const response = await axios.get<Assignment[]>( |
81 |
| - `${CONFIG.CANVAS_DOMAIN}courses/${courseId}/assignments`, |
82 |
| - { |
83 |
| - headers: { |
84 |
| - Authorization: `Bearer ${canvasToken}`, |
85 |
| - "Content-Type": "application/json", |
86 |
| - }, |
87 |
| - }, |
| 63 | + const assignments = await fetchData<Assignment[]>( |
| 64 | + userId, |
| 65 | + `courses/${courseId}/assignments`, |
88 | 66 | );
|
89 |
| - console.log("Assignments fetched successfully for course ID:", courseId); |
90 |
| - return response.data || []; |
| 67 | + return { |
| 68 | + message: "Assignments fetched successfully", |
| 69 | + assignments: assignments, |
| 70 | + }; |
91 | 71 | } catch (error) {
|
92 |
| - if (axios.isAxiosError(error)) { |
93 |
| - console.error( |
94 |
| - "Axios error fetching assignments for the course:", |
95 |
| - error.message, |
96 |
| - ); |
97 |
| - } else { |
98 |
| - console.error( |
99 |
| - "Unexpected error fetching assignments for the course:", |
100 |
| - error, |
101 |
| - ); |
102 |
| - } |
103 |
| - return []; |
| 72 | + console.error(`Error fetching assignments for course ${courseId}:`, error); |
| 73 | + return { |
| 74 | + message: "Failed to fetch assignments", |
| 75 | + assignments: [], |
| 76 | + }; |
104 | 77 | }
|
105 | 78 | }
|
106 | 79 |
|
107 | 80 | export async function getAllAssignments(
|
108 | 81 | userId: string,
|
109 |
| -): Promise<MissingAssignmentResponse> { |
| 82 | +): Promise<{ message: string; courses: Course[] }> { |
110 | 83 | try {
|
111 |
| - const canvasToken = await getCanvasToken(userId); |
112 |
| - if (!canvasToken) { |
113 |
| - return { |
114 |
| - message: |
115 |
| - "You are not enrolled in any courses; Please enter your token with the command /account", |
116 |
| - courses: [], |
117 |
| - }; |
118 |
| - } |
119 |
| - |
120 |
| - const headers = { |
121 |
| - Authorization: `Bearer ${canvasToken}`, |
122 |
| - "Content-Type": "application/json", |
123 |
| - }; |
124 |
| - |
125 |
| - const params = { |
126 |
| - enrollment_type: "student", |
127 |
| - enrollment_state: "active", |
128 |
| - user_id: userId, |
129 |
| - }; |
130 |
| - |
131 |
| - const res = await axios.get<Course[]>( |
132 |
| - `${CONFIG.CANVAS_DOMAIN}users/self/missing_submissions?include[]=planner_overrides&filter[]=current_grading_period&filter[]=submittable`, |
133 |
| - { headers, params }, |
| 84 | + const courses = await fetchData<Course[]>( |
| 85 | + userId, |
| 86 | + "users/self/missing_submissions", |
| 87 | + { |
| 88 | + include: "planner_overrides", |
| 89 | + filter: "current_grading_period_id,submittable", |
| 90 | + }, |
134 | 91 | );
|
135 |
| - |
136 | 92 | return {
|
137 |
| - message: "Missing Courses fetched successfully", |
138 |
| - courses: res.data || [], |
| 93 | + message: "Missing submissions fetched successfully", |
| 94 | + courses: courses, |
139 | 95 | };
|
140 | 96 | } catch (error) {
|
141 |
| - console.error( |
142 |
| - "Error fetching user's missing courses from Canvas:", |
143 |
| - error instanceof Error ? error.message : String(error), |
144 |
| - ); |
| 97 | + console.error("Failed to fetch missing submissions:", error); |
145 | 98 | return {
|
146 |
| - message: "An error occurred while fetching courses.", |
| 99 | + message: "Failed to fetch missing submissions", |
147 | 100 | courses: [],
|
148 | 101 | };
|
149 | 102 | }
|
150 | 103 | }
|
151 | 104 |
|
152 |
| -export async function getCourses( |
153 |
| - canvasToken: string, |
154 |
| - userID: string, |
155 |
| -): Promise<Course[]> { |
| 105 | +export async function getCourses(userId: string): Promise<Course[]> { |
156 | 106 | try {
|
157 |
| - const canvasID = await getCanvasID(userID); |
158 |
| - const response = await axios.get<Course[]>( |
159 |
| - `${CONFIG.CANVAS_DOMAIN}users/${canvasID}/courses`, |
160 |
| - { |
161 |
| - headers: { |
162 |
| - Authorization: `Bearer ${canvasToken}`, |
163 |
| - }, |
164 |
| - }, |
165 |
| - ); |
166 |
| - return response.data; |
| 107 | + const canvasID = await getCanvasID(userId); |
| 108 | + return await fetchData<Course[]>(userId, `users/${canvasID}/courses`); |
167 | 109 | } catch (error) {
|
168 | 110 | console.error("Failed to fetch courses:", error);
|
169 | 111 | throw new Error("Error fetching courses.");
|
170 | 112 | }
|
171 | 113 | }
|
172 | 114 |
|
173 | 115 | export async function getAllAnnouncements(
|
174 |
| - canvasToken: string, |
175 |
| - userID: string, |
| 116 | + userId: string, |
176 | 117 | ): Promise<AnnouncementPost[]> {
|
177 | 118 | try {
|
178 |
| - const courses = await getCourses(canvasToken, userID); |
| 119 | + const courses = await getCourses(userId); |
179 | 120 | const today = new Date();
|
180 | 121 | today.setHours(0, 0, 0, 0);
|
181 | 122 |
|
182 | 123 | let allAnnouncements: AnnouncementPost[] = [];
|
183 | 124 |
|
184 | 125 | for (const course of courses) {
|
185 | 126 | try {
|
186 |
| - const response = await axios.get<AnnouncementPost[]>( |
187 |
| - `${CONFIG.CANVAS_DOMAIN}announcements?context_codes[]=course_${course.id}`, |
188 |
| - { |
189 |
| - headers: { |
190 |
| - Authorization: `Bearer ${canvasToken}`, |
191 |
| - }, |
192 |
| - }, |
| 127 | + const announcements = await fetchData<AnnouncementPost[]>( |
| 128 | + userId, |
| 129 | + "announcements", |
| 130 | + { context_codes: `course_${course.id}` }, |
193 | 131 | );
|
194 | 132 |
|
195 |
| - const filteredAnnouncements = response.data |
| 133 | + const filteredAnnouncements = announcements |
196 | 134 | .filter((announcement) => {
|
197 | 135 | const announcementDate = new Date(announcement.posted_at || "");
|
198 | 136 | return announcementDate >= today;
|
@@ -223,39 +161,27 @@ export async function getAllAnnouncements(
|
223 | 161 | export async function fetchAssignmentChecker(
|
224 | 162 | userId: string,
|
225 | 163 | ): Promise<Assignment[]> {
|
226 |
| - const canvasToken = await getCanvasToken(userId); |
227 |
| - if (!canvasToken) { |
228 |
| - throw new Error("Canvas token is null or undefined."); |
229 |
| - } |
230 |
| - const courses = await getCourses(canvasToken, userId); |
231 |
| - |
232 |
| - const today = new Date(); |
233 |
| - today.setHours(0, 0, 0, 0); |
234 |
| - const tomorrow = new Date(today); |
235 |
| - tomorrow.setDate(tomorrow.getDate() + 1); |
236 |
| - tomorrow.setHours(23, 59, 59, 999); |
237 |
| - |
238 |
| - let allAssignments: Assignment[] = []; |
| 164 | + try { |
| 165 | + const courses = await getCourses(userId); |
239 | 166 |
|
240 |
| - await Promise.all( |
241 |
| - courses.map(async (course) => { |
242 |
| - const { data: assignments } = await axios.get<Assignment[]>( |
243 |
| - `${CONFIG.CANVAS_DOMAIN}courses/${course.id}/assignments`, |
244 |
| - { |
245 |
| - headers: { |
246 |
| - Authorization: `Bearer ${canvasToken}`, |
247 |
| - }, |
248 |
| - }, |
249 |
| - ); |
| 167 | + const today = new Date(); |
| 168 | + today.setHours(0, 0, 0, 0); |
| 169 | + const tomorrow = new Date(today); |
| 170 | + tomorrow.setDate(tomorrow.getDate() + 1); |
| 171 | + tomorrow.setHours(23, 59, 59, 999); |
250 | 172 |
|
251 |
| - const filteredAssignments = assignments.filter((assignment) => { |
252 |
| - const dueDate = new Date(assignment.due_at); |
253 |
| - return dueDate >= today && dueDate <= tomorrow; |
254 |
| - }); |
| 173 | + const assignmentPromises = courses.map((course) => |
| 174 | + fetchData<Assignment[]>(userId, `courses/${course.id}/assignments`), |
| 175 | + ); |
255 | 176 |
|
256 |
| - allAssignments = allAssignments.concat(filteredAssignments); |
257 |
| - }), |
258 |
| - ); |
| 177 | + const allAssignments = await Promise.all(assignmentPromises); |
259 | 178 |
|
260 |
| - return allAssignments; |
| 179 | + return allAssignments.flat().filter((assignment) => { |
| 180 | + const dueDate = new Date(assignment.due_at); |
| 181 | + return dueDate >= today && dueDate <= tomorrow; |
| 182 | + }); |
| 183 | + } catch (error) { |
| 184 | + console.error("Error fetching assignments for next day:", error); |
| 185 | + return []; |
| 186 | + } |
261 | 187 | }
|
0 commit comments