Skip to content

Commit 8d2a35f

Browse files
authored
Merge pull request #4 from CCU-Class/batch_request
add batch insert event to calendar
2 parents d47b116 + 463d927 commit 8d2a35f

File tree

2 files changed

+117
-9
lines changed

2 files changed

+117
-9
lines changed

src/js/popup.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { getCurrentYearMonth, downloadICS, addOneEventToCalendar } from "./utils.js";
1+
import {
2+
getCurrentYearMonth,
3+
downloadICS,
4+
addOneEventToCalendar,
5+
addBatchEventsToCalendar,
6+
} from "./utils.js";
27

38
function initializeInputs() {
49
const { year, month } = getCurrentYearMonth();
@@ -112,18 +117,28 @@ function setupEventListeners() {
112117
async function insertEventsToGCal(events) {
113118
// Google OAuth token
114119
const token = await getGoogleAuthToken();
115-
116-
// add event Google Calendar
117-
for (const event of events) {
120+
const batchAddEvents = true;
121+
122+
if (!batchAddEvents) {
123+
// add event Google Calendar
124+
for (const event of events) {
125+
try {
126+
await addOneEventToCalendar(token, event);
127+
console.log("add Event Success", event.title);
128+
} catch (err) {
129+
console.error("add Event error:", event.title, err);
130+
}
131+
}
132+
} else if (batchAddEvents) {
133+
// batch API,done after...
118134
try {
119-
await addOneEventToCalendar(token, event);
120-
console.log("add Event Success", event.title);
135+
const batchResponse = await addBatchEventsToCalendar(token, events);
136+
console.log("Batch request sent!");
137+
console.log(batchResponse);
121138
} catch (err) {
122-
console.error("add Event error:", event.title, err);
139+
console.error("Batch request failed:", err);
123140
}
124141
}
125-
126-
// batch API,done after...
127142
}
128143

129144
async function launchWebAuthFlowForGoogle() {

src/js/utils.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,96 @@ export async function addOneEventToCalendar(token, event) {
7373

7474
return res.json();
7575
}
76+
77+
function normalizeBatchText(raw) {
78+
return raw.replace(/\r?\n/g, "\r\n");
79+
}
80+
81+
function generateBatchRequestBody(events, boundary = "batch_boundary") {
82+
const CRLF = "\r\n";
83+
let body = "";
84+
85+
events.forEach((event, i) => {
86+
body += `--${boundary}${CRLF}`;
87+
body += `Content-Type: application/http${CRLF}`;
88+
body += `Content-ID: <item-${i + 1}>${CRLF}${CRLF}`;
89+
90+
body += `POST /calendar/v3/calendars/primary/events HTTP/1.1${CRLF}`;
91+
body += `Content-Type: application/json${CRLF}${CRLF}`;
92+
93+
const eventPayload = {
94+
summary: event.title,
95+
description: event.description,
96+
start: {
97+
dateTime: event.startDate.toISOString(),
98+
timeZone: "Asia/Taipei",
99+
},
100+
end: {
101+
dateTime: event.endDate.toISOString(),
102+
timeZone: "Asia/Taipei",
103+
},
104+
};
105+
106+
body += JSON.stringify(eventPayload) + CRLF + CRLF;
107+
});
108+
109+
body += `--${boundary}--${CRLF}`;
110+
return body;
111+
}
112+
113+
function parseGoogleBatchResponse(responseText) {
114+
const boundary = responseText.match(/--batch_[^\r\n-]+/)?.[0]?.slice(2);
115+
const parts = responseText.split(`--${boundary}`);
116+
const results = [];
117+
118+
for (const part of parts) {
119+
const trimmed = part.trim();
120+
if (!trimmed || !trimmed.includes("HTTP/1.1")) continue;
121+
122+
const statusMatch = trimmed.match(/HTTP\/1.1 (\d{3}) (.+)/);
123+
const status = statusMatch ? parseInt(statusMatch[1], 10) : null;
124+
const statusText = statusMatch ? statusMatch[2] : "";
125+
126+
const sections = trimmed.split(/\n{2,}|\r\n{2,}/);
127+
const rawBody = sections[sections.length - 1];
128+
129+
const jsonStart = rawBody.indexOf("{");
130+
const jsonEnd = rawBody.lastIndexOf("}");
131+
132+
let body = null;
133+
if (jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart) {
134+
const jsonStr = rawBody.slice(jsonStart, jsonEnd + 1);
135+
try {
136+
body = JSON.parse(jsonStr);
137+
} catch (e) {
138+
console.warn("JSON parse failed:", e);
139+
}
140+
}
141+
142+
results.push({ status, ok: status >= 200 && status < 300, statusText, body });
143+
}
144+
145+
return results;
146+
}
147+
148+
// Google Calendar API batch add Event
149+
export async function addBatchEventsToCalendar(token, events) {
150+
const boundary = "batch_boundary_" + Date.now();
151+
const body = generateBatchRequestBody(events, boundary);
152+
153+
const res = await fetch("https://www.googleapis.com/batch/calendar/v3", {
154+
method: "POST",
155+
headers: {
156+
Authorization: `Bearer ${token}`,
157+
"Content-Type": `multipart/mixed; boundary=${boundary}`,
158+
},
159+
body,
160+
});
161+
162+
const text = await res.text();
163+
if (!res.ok) {
164+
throw new Error(`Batch API Error: ${res.status}\n${text}`);
165+
}
166+
167+
return parseGoogleBatchResponse(normalizeBatchText(text));
168+
}

0 commit comments

Comments
 (0)