@timesheet/plugin-google-calendar 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -64,14 +64,28 @@ class GoogleCalendarClient {
|
|
|
64
64
|
}
|
|
65
65
|
async stopWatch(channelId, resourceId) {
|
|
66
66
|
const token = await this.getAccessToken();
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timeoutId = setTimeout(() => controller.abort(), GoogleCalendarClient.REQUEST_TIMEOUT_MS);
|
|
69
|
+
let response;
|
|
70
|
+
try {
|
|
71
|
+
response = await fetch('https://www.googleapis.com/calendar/v3/channels/stop', {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: {
|
|
74
|
+
Authorization: `Bearer ${token}`,
|
|
75
|
+
'Content-Type': 'application/json'
|
|
76
|
+
},
|
|
77
|
+
body: JSON.stringify({ id: channelId, resourceId }),
|
|
78
|
+
signal: controller.signal
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
clearTimeout(timeoutId);
|
|
83
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
84
|
+
throw new Error(`Google Calendar API channels/stop timed out after ${GoogleCalendarClient.REQUEST_TIMEOUT_MS}ms`);
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
clearTimeout(timeoutId);
|
|
75
89
|
// 404 means channel already expired — not an error
|
|
76
90
|
if (!response.ok && response.status !== 404) {
|
|
77
91
|
const errorText = await response.text();
|
|
@@ -80,15 +94,29 @@ class GoogleCalendarClient {
|
|
|
80
94
|
}
|
|
81
95
|
async request(method, path, query, body, retried = false) {
|
|
82
96
|
const token = await this.getAccessToken();
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
const controller = new AbortController();
|
|
98
|
+
const timeoutId = setTimeout(() => controller.abort(), GoogleCalendarClient.REQUEST_TIMEOUT_MS);
|
|
99
|
+
let response;
|
|
100
|
+
try {
|
|
101
|
+
response = await fetch(this.buildUrl(path, query), {
|
|
102
|
+
method,
|
|
103
|
+
headers: {
|
|
104
|
+
Authorization: `Bearer ${token}`,
|
|
105
|
+
Accept: 'application/json',
|
|
106
|
+
'Content-Type': 'application/json'
|
|
107
|
+
},
|
|
108
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
109
|
+
signal: controller.signal
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
115
|
+
throw new Error(`Google Calendar API ${method} ${path} timed out after ${GoogleCalendarClient.REQUEST_TIMEOUT_MS}ms`);
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
clearTimeout(timeoutId);
|
|
92
120
|
if (response.status === 401 && !retried) {
|
|
93
121
|
const refreshed = await this.refreshAccessToken();
|
|
94
122
|
if (refreshed) {
|
|
@@ -118,3 +146,4 @@ class GoogleCalendarClient {
|
|
|
118
146
|
}
|
|
119
147
|
}
|
|
120
148
|
exports.GoogleCalendarClient = GoogleCalendarClient;
|
|
149
|
+
GoogleCalendarClient.REQUEST_TIMEOUT_MS = 30000;
|
package/dist/lib/taskSync.js
CHANGED
|
@@ -194,6 +194,21 @@ async function syncCalendar(context, projectMapping) {
|
|
|
194
194
|
if (!syncToken && metadataSyncToken) {
|
|
195
195
|
syncToken = metadataSyncToken;
|
|
196
196
|
}
|
|
197
|
+
try {
|
|
198
|
+
return await fetchAndSyncEvents(context, client, projectMapping, calendarId, syncStateKey, syncToken);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
// Google returns 400 or 410 when a sync token is invalid/expired — clear it and do a full resync
|
|
202
|
+
const errMsg = String(err);
|
|
203
|
+
if (syncToken && (errMsg.includes('(410)') || errMsg.includes('Invalid sync token'))) {
|
|
204
|
+
context.logger.warn('Sync token expired, performing full resync', { calendarId });
|
|
205
|
+
await context.state.delete(syncStateKey);
|
|
206
|
+
return await fetchAndSyncEvents(context, client, projectMapping, calendarId, syncStateKey, undefined);
|
|
207
|
+
}
|
|
208
|
+
throw err;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function fetchAndSyncEvents(context, client, projectMapping, calendarId, syncStateKey, syncToken) {
|
|
197
212
|
let pageToken;
|
|
198
213
|
let nextSyncToken;
|
|
199
214
|
let syncedCount = 0;
|
|
@@ -411,11 +426,7 @@ function getHeader(input, name) {
|
|
|
411
426
|
return value === undefined || value === null ? undefined : String(value);
|
|
412
427
|
}
|
|
413
428
|
async function ensureWatchChannels(context) {
|
|
414
|
-
|
|
415
|
-
const contextAny = context;
|
|
416
|
-
const metadata = contextAny['metadata'];
|
|
417
|
-
const webhooks = metadata?.['webhooks'];
|
|
418
|
-
const webhookUrl = webhooks?.['integration-webhook'];
|
|
429
|
+
const webhookUrl = context.metadata?.webhooks?.['integration-webhook'];
|
|
419
430
|
if (!webhookUrl) {
|
|
420
431
|
context.logger.info('No webhook URL available — skipping watch channel registration');
|
|
421
432
|
return;
|
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "google-calendar-sync",
|
|
3
3
|
"name": "Google Calendar Sync",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"description": "Synchronize Timesheet tasks with Google Calendar",
|
|
6
6
|
"longDescription": "Bidirectional synchronization between Timesheet tasks and Google Calendar events.",
|
|
7
7
|
"icon": "google-calendar",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timesheet/plugin-google-calendar",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"prepublishOnly": "npm run build"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@timesheet/integration-sdk": "^0.
|
|
16
|
+
"@timesheet/integration-sdk": "^0.3.0"
|
|
17
17
|
}
|
|
18
18
|
}
|