@kubun/connector-google-calendar 0.17.2 → 0.17.3
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.
- package/lib/api.d.ts +2 -1
- package/lib/api.js +19 -60
- package/lib/errors.d.ts +0 -10
- package/lib/errors.js +0 -15
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/model.d.ts +13 -13
- package/lib/model.js +4 -24
- package/lib/provider.js +56 -99
- package/package.json +4 -4
package/lib/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type EntityRecord } from '@kubun/connector';
|
|
2
|
+
import { type GoogleAPIClient } from '@kubun/provider-google';
|
|
2
3
|
export { type ConnectorErrorKind, classifyHTTPStatus } from '@kubun/connector';
|
|
3
|
-
export declare function
|
|
4
|
+
export declare function calendarAPIClient(accessToken: string, fetch?: typeof globalThis.fetch): GoogleAPIClient;
|
|
4
5
|
export type GoogleCalendarResource = {
|
|
5
6
|
id: string;
|
|
6
7
|
summary: string;
|
package/lib/api.js
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { connectorHTTPError } from '@kubun/connector';
|
|
2
|
+
import { createGoogleAPIClient } from '@kubun/provider-google';
|
|
2
3
|
import { GoogleCalendarEventConflict } from './errors.js';
|
|
3
4
|
import { eventHoldsIntent } from './intent.js';
|
|
4
5
|
export { classifyHTTPStatus } from '@kubun/connector';
|
|
5
6
|
const CALENDAR_BASE_URL = 'https://www.googleapis.com/calendar/v3';
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Build the error thrown by an action's provider call, tagged with the HTTP
|
|
16
|
-
* status and its outcome classification so the caller can distinguish a
|
|
17
|
-
* definite rejection from an indeterminate failure.
|
|
18
|
-
*/ function googleAPIError(status, detail) {
|
|
19
|
-
return connectorHTTPError(`Google Calendar API error: ${detail}`, status);
|
|
7
|
+
export function calendarAPIClient(accessToken, fetch) {
|
|
8
|
+
return createGoogleAPIClient({
|
|
9
|
+
api: 'Google Calendar API',
|
|
10
|
+
accessToken,
|
|
11
|
+
fetch,
|
|
12
|
+
toError: ({ httpStatus, message })=>connectorHTTPError(message, httpStatus)
|
|
13
|
+
});
|
|
20
14
|
}
|
|
21
15
|
/**
|
|
22
16
|
* A create that collided with an existing event it could not confirm as its
|
|
@@ -158,18 +152,8 @@ export function buildEventFromParams(params, options) {
|
|
|
158
152
|
// colour. calendars.get carries neither, so a re-pull from it would clear
|
|
159
153
|
// `primary` and break resolving the 'primary' alias.
|
|
160
154
|
export async function getCalendarListEntry(config) {
|
|
161
|
-
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
162
155
|
const url = `${CALENDAR_BASE_URL}/users/me/calendarList/${encodeURIComponent(config.calendarID)}`;
|
|
163
|
-
|
|
164
|
-
headers: {
|
|
165
|
-
Authorization: `Bearer ${config.accessToken}`
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
if (!response.ok) {
|
|
169
|
-
const errorMessage = await parseGoogleError(response);
|
|
170
|
-
throw googleAPIError(response.status, errorMessage);
|
|
171
|
-
}
|
|
172
|
-
return await response.json();
|
|
156
|
+
return calendarAPIClient(config.accessToken, config.fetch).get(url);
|
|
173
157
|
}
|
|
174
158
|
// A composite calendarID/eventID sourceID splits on the LAST '/': an eventID
|
|
175
159
|
// never contains one, but a calendarID (email-like) may.
|
|
@@ -184,18 +168,8 @@ export function splitEventSourceID(sourceID) {
|
|
|
184
168
|
};
|
|
185
169
|
}
|
|
186
170
|
async function fetchEventResource(config) {
|
|
187
|
-
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
188
171
|
const url = `${CALENDAR_BASE_URL}/calendars/${encodeURIComponent(config.calendarID)}/events/${encodeURIComponent(config.eventID)}`;
|
|
189
|
-
|
|
190
|
-
headers: {
|
|
191
|
-
Authorization: `Bearer ${config.accessToken}`
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
if (!response.ok) {
|
|
195
|
-
const errorMessage = await parseGoogleError(response);
|
|
196
|
-
throw googleAPIError(response.status, errorMessage);
|
|
197
|
-
}
|
|
198
|
-
return await response.json();
|
|
172
|
+
return calendarAPIClient(config.accessToken, config.fetch).get(url);
|
|
199
173
|
}
|
|
200
174
|
export async function getEvent(config) {
|
|
201
175
|
const data = await fetchEventResource(config);
|
|
@@ -212,20 +186,16 @@ export async function getEventWithETag(config) {
|
|
|
212
186
|
};
|
|
213
187
|
}
|
|
214
188
|
export async function createEvent(config) {
|
|
215
|
-
const
|
|
189
|
+
const client = calendarAPIClient(config.accessToken, config.fetch);
|
|
216
190
|
const event = buildEventFromParams(config.params, {
|
|
217
191
|
create: true,
|
|
218
192
|
eventID: config.eventID
|
|
219
193
|
});
|
|
220
194
|
const eventID = event.id;
|
|
221
195
|
const url = `${CALENDAR_BASE_URL}/calendars/${encodeURIComponent(config.calendarID)}/events`;
|
|
222
|
-
const response = await
|
|
196
|
+
const response = await client.request(url, {
|
|
223
197
|
method: 'POST',
|
|
224
|
-
|
|
225
|
-
Authorization: `Bearer ${config.accessToken}`,
|
|
226
|
-
'Content-Type': 'application/json'
|
|
227
|
-
},
|
|
228
|
-
body: JSON.stringify(event)
|
|
198
|
+
body: event
|
|
229
199
|
});
|
|
230
200
|
if (!response.ok) {
|
|
231
201
|
// A 409 on a caller-chosen id means an event already exists under it. It
|
|
@@ -254,31 +224,20 @@ export async function createEvent(config) {
|
|
|
254
224
|
}
|
|
255
225
|
return existing;
|
|
256
226
|
}
|
|
257
|
-
|
|
258
|
-
throw googleAPIError(response.status, errorMessage);
|
|
227
|
+
return client.fail(response);
|
|
259
228
|
}
|
|
260
229
|
const data = await response.json();
|
|
261
230
|
return mapEventToEntity(data, config.calendarID);
|
|
262
231
|
}
|
|
263
232
|
export async function updateEvent(config) {
|
|
264
|
-
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
265
233
|
const event = buildEventFromParams(config.params);
|
|
266
234
|
const url = `${CALENDAR_BASE_URL}/calendars/${encodeURIComponent(config.calendarID)}/events/${encodeURIComponent(config.eventID)}`;
|
|
267
|
-
const
|
|
235
|
+
const data = await calendarAPIClient(config.accessToken, config.fetch).send(url, {
|
|
268
236
|
method: 'PATCH',
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
'
|
|
272
|
-
|
|
273
|
-
'If-Match': config.etag
|
|
274
|
-
} : {}
|
|
275
|
-
},
|
|
276
|
-
body: JSON.stringify(event)
|
|
237
|
+
body: event,
|
|
238
|
+
headers: config.etag != null ? {
|
|
239
|
+
'If-Match': config.etag
|
|
240
|
+
} : {}
|
|
277
241
|
});
|
|
278
|
-
if (!response.ok) {
|
|
279
|
-
const errorMessage = await parseGoogleError(response);
|
|
280
|
-
throw googleAPIError(response.status, errorMessage);
|
|
281
|
-
}
|
|
282
|
-
const data = await response.json();
|
|
283
242
|
return mapEventToEntity(data, config.calendarID);
|
|
284
243
|
}
|
package/lib/errors.d.ts
CHANGED
|
@@ -20,13 +20,3 @@ export declare class GoogleCalendarEventConflict extends GoogleCalendarError {
|
|
|
20
20
|
constructor(sourceID: string, options?: ErrorOptions);
|
|
21
21
|
get sourceID(): string;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
24
|
-
* A raw provider HTTP failure from a batch/list round trip (`fetchJSON` in
|
|
25
|
-
* `provider.ts`), carrying the response status so `fetchChanges` can detect
|
|
26
|
-
* an expired sync token (410) and fall back to a full resync.
|
|
27
|
-
*/
|
|
28
|
-
export declare class GoogleCalendarAPIError extends Error {
|
|
29
|
-
#private;
|
|
30
|
-
constructor(status: number, detail: string);
|
|
31
|
-
get status(): number;
|
|
32
|
-
}
|
package/lib/errors.js
CHANGED
|
@@ -29,18 +29,3 @@ import { ConnectorError } from '@kubun/connector';
|
|
|
29
29
|
return this.#sourceID;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* A raw provider HTTP failure from a batch/list round trip (`fetchJSON` in
|
|
34
|
-
* `provider.ts`), carrying the response status so `fetchChanges` can detect
|
|
35
|
-
* an expired sync token (410) and fall back to a full resync.
|
|
36
|
-
*/ export class GoogleCalendarAPIError extends Error {
|
|
37
|
-
#status;
|
|
38
|
-
constructor(status, detail){
|
|
39
|
-
super(`Google Calendar API error: ${detail}`);
|
|
40
|
-
this.name = 'GoogleCalendarAPIError';
|
|
41
|
-
this.#status = status;
|
|
42
|
-
}
|
|
43
|
-
get status() {
|
|
44
|
-
return this.#status;
|
|
45
|
-
}
|
|
46
|
-
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createGoogleCalendarActionHandler, type GoogleCalendarActionHandlerConfig, } from './actions.js';
|
|
2
2
|
export { buildCalendarListURL, buildEventFromParams, buildEventsListURL, createEvent, type GoogleCalendarListResponse, type GoogleCalendarResource, type GoogleEventDateTime, type GoogleEventResource, type GoogleEventsListResponse, mapCalendarToEntity, mapEventToEntity, updateEvent, } from './api.js';
|
|
3
3
|
export { type CreateGoogleCalendarConnectorOptions, createGoogleCalendarConnector, } from './connector.js';
|
|
4
|
-
export {
|
|
4
|
+
export { GoogleCalendarError, type GoogleCalendarErrorCode, type GoogleCalendarErrorOptions, GoogleCalendarEventConflict, } from './errors.js';
|
|
5
5
|
export { deriveEventID } from './event-id.js';
|
|
6
6
|
export { canonicalizeEventIntent, eventHoldsIntent } from './intent.js';
|
|
7
7
|
export { googleCalendarClusters, googleCalendarDefinition, googleCalendarEventDefinition, } from './model.js';
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createGoogleCalendarActionHandler } from './actions.js';
|
|
2
2
|
export { buildCalendarListURL, buildEventFromParams, buildEventsListURL, createEvent, mapCalendarToEntity, mapEventToEntity, updateEvent } from './api.js';
|
|
3
3
|
export { createGoogleCalendarConnector } from './connector.js';
|
|
4
|
-
export {
|
|
4
|
+
export { GoogleCalendarError, GoogleCalendarEventConflict } from './errors.js';
|
|
5
5
|
export { deriveEventID } from './event-id.js';
|
|
6
6
|
export { canonicalizeEventIntent, eventHoldsIntent } from './intent.js';
|
|
7
7
|
export { googleCalendarClusters, googleCalendarDefinition, googleCalendarEventDefinition } from './model.js';
|
package/lib/model.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export declare const googleCalendarDefinition: {
|
|
|
41
41
|
readonly required: ["summary", "sourceService", "sourceID", "lastSyncedAt"];
|
|
42
42
|
readonly additionalProperties: false;
|
|
43
43
|
readonly definitions: {
|
|
44
|
+
readonly TimeZone: import("json-schema-typed").JSONSchema.String;
|
|
44
45
|
readonly SourceService: {
|
|
45
46
|
readonly type: "string";
|
|
46
47
|
readonly title: "SourceService";
|
|
@@ -59,7 +60,6 @@ export declare const googleCalendarDefinition: {
|
|
|
59
60
|
readonly title: "URL";
|
|
60
61
|
};
|
|
61
62
|
readonly DateTime: import("json-schema-typed").JSONSchema.String;
|
|
62
|
-
readonly TimeZone: import("json-schema-typed").JSONSchema.String;
|
|
63
63
|
};
|
|
64
64
|
};
|
|
65
65
|
readonly fieldsMeta: {};
|
|
@@ -131,24 +131,12 @@ export declare const googleCalendarEventDefinition: {
|
|
|
131
131
|
readonly required: ["title", "startAt", "allDay", "calendarID", "calendar", "sourceService", "sourceID", "lastSyncedAt"];
|
|
132
132
|
readonly additionalProperties: false;
|
|
133
133
|
readonly definitions: {
|
|
134
|
-
readonly SourceService: {
|
|
135
|
-
readonly type: "string";
|
|
136
|
-
readonly title: "SourceService";
|
|
137
|
-
readonly minLength: 1;
|
|
138
|
-
readonly maxLength: 50;
|
|
139
|
-
};
|
|
140
134
|
readonly SourceID: {
|
|
141
135
|
readonly type: "string";
|
|
142
136
|
readonly title: "SourceID";
|
|
143
137
|
readonly minLength: 1;
|
|
144
138
|
readonly maxLength: 1400;
|
|
145
139
|
};
|
|
146
|
-
readonly SourceURL: {
|
|
147
|
-
readonly type: "string";
|
|
148
|
-
readonly format: "uri";
|
|
149
|
-
readonly title: "URL";
|
|
150
|
-
};
|
|
151
|
-
readonly DateTime: import("json-schema-typed").JSONSchema.String;
|
|
152
140
|
readonly TimeZone: import("json-schema-typed").JSONSchema.String;
|
|
153
141
|
readonly HtmlLink: {
|
|
154
142
|
readonly type: "string";
|
|
@@ -165,6 +153,18 @@ export declare const googleCalendarEventDefinition: {
|
|
|
165
153
|
readonly title: "EventStatus";
|
|
166
154
|
readonly enum: ["CONFIRMED", "TENTATIVE", "CANCELLED"];
|
|
167
155
|
};
|
|
156
|
+
readonly SourceService: {
|
|
157
|
+
readonly type: "string";
|
|
158
|
+
readonly title: "SourceService";
|
|
159
|
+
readonly minLength: 1;
|
|
160
|
+
readonly maxLength: 50;
|
|
161
|
+
};
|
|
162
|
+
readonly SourceURL: {
|
|
163
|
+
readonly type: "string";
|
|
164
|
+
readonly format: "uri";
|
|
165
|
+
readonly title: "URL";
|
|
166
|
+
};
|
|
167
|
+
readonly DateTime: import("json-schema-typed").JSONSchema.String;
|
|
168
168
|
};
|
|
169
169
|
};
|
|
170
170
|
readonly fieldsMeta: {
|
package/lib/model.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { connectorClusters, connectorModelIDs } from '@kubun/connector';
|
|
1
|
+
import { connectorClusters, connectorModelIDs, externalEntityDefinition } from '@kubun/connector';
|
|
2
2
|
import { modelIDs, modelsClusters } from '@kubun/models';
|
|
3
3
|
import { GraphBuilder } from '@kubun/protocol';
|
|
4
|
-
import {
|
|
4
|
+
import { docIDSchema, timeZoneSchema, urlSchema } from '@kubun/scalars';
|
|
5
5
|
export const googleCalendarDefinition = {
|
|
6
6
|
name: 'GoogleCalendar',
|
|
7
7
|
behavior: 'default',
|
|
@@ -52,20 +52,7 @@ export const googleCalendarDefinition = {
|
|
|
52
52
|
],
|
|
53
53
|
additionalProperties: false,
|
|
54
54
|
definitions: {
|
|
55
|
-
|
|
56
|
-
type: 'string',
|
|
57
|
-
title: 'SourceService',
|
|
58
|
-
minLength: 1,
|
|
59
|
-
maxLength: 50
|
|
60
|
-
},
|
|
61
|
-
SourceID: {
|
|
62
|
-
type: 'string',
|
|
63
|
-
title: 'SourceID',
|
|
64
|
-
minLength: 1,
|
|
65
|
-
maxLength: 500
|
|
66
|
-
},
|
|
67
|
-
SourceURL: urlSchema,
|
|
68
|
-
DateTime: dateTimeSchema,
|
|
55
|
+
...externalEntityDefinition.schema.definitions,
|
|
69
56
|
TimeZone: timeZoneSchema
|
|
70
57
|
}
|
|
71
58
|
},
|
|
@@ -150,12 +137,7 @@ export const googleCalendarEventDefinition = {
|
|
|
150
137
|
],
|
|
151
138
|
additionalProperties: false,
|
|
152
139
|
definitions: {
|
|
153
|
-
|
|
154
|
-
type: 'string',
|
|
155
|
-
title: 'SourceService',
|
|
156
|
-
minLength: 1,
|
|
157
|
-
maxLength: 50
|
|
158
|
-
},
|
|
140
|
+
...externalEntityDefinition.schema.definitions,
|
|
159
141
|
// A composite calendarID/eventID: calendar id (email-like, up to ~254) +
|
|
160
142
|
// '/' + a Google event id (up to 1024). 500 is too small.
|
|
161
143
|
SourceID: {
|
|
@@ -164,8 +146,6 @@ export const googleCalendarEventDefinition = {
|
|
|
164
146
|
minLength: 1,
|
|
165
147
|
maxLength: 1400
|
|
166
148
|
},
|
|
167
|
-
SourceURL: urlSchema,
|
|
168
|
-
DateTime: dateTimeSchema,
|
|
169
149
|
TimeZone: timeZoneSchema,
|
|
170
150
|
HtmlLink: urlSchema,
|
|
171
151
|
CalendarDocID: docIDSchema,
|
package/lib/provider.js
CHANGED
|
@@ -1,30 +1,12 @@
|
|
|
1
|
-
import { resolveModelID } from '@kubun/connector';
|
|
1
|
+
import { ConnectorError, fetchOrAbsent, parseMaxAgeDays, resolveModelID } from '@kubun/connector';
|
|
2
2
|
import { deriveAccountKey } from './account-key.js';
|
|
3
|
-
import { buildCalendarListURL, buildEventsListURL, getCalendarListEntry, getEvent, getEventWithETag, mapCalendarToEntity, mapEventToEntity,
|
|
4
|
-
import { GoogleCalendarAPIError } from './errors.js';
|
|
3
|
+
import { buildCalendarListURL, buildEventsListURL, calendarAPIClient, getCalendarListEntry, getEvent, getEventWithETag, mapCalendarToEntity, mapEventToEntity, splitEventSourceID } from './api.js';
|
|
5
4
|
import { googleCalendarClusters } from './model.js';
|
|
6
5
|
const CALENDAR_MODEL_ID = resolveModelID(googleCalendarClusters, 'GoogleCalendar');
|
|
7
6
|
const CALENDAR_EVENT_MODEL_ID = resolveModelID(googleCalendarClusters, 'GoogleCalendarEvent');
|
|
8
|
-
async function fetchJSON(url, credential, fetchFn, signal) {
|
|
9
|
-
const response = await fetchFn(url, {
|
|
10
|
-
headers: {
|
|
11
|
-
Authorization: `Bearer ${credential.accessToken}`
|
|
12
|
-
},
|
|
13
|
-
signal
|
|
14
|
-
});
|
|
15
|
-
if (!response.ok) {
|
|
16
|
-
throw new GoogleCalendarAPIError(response.status, await parseGoogleError(response));
|
|
17
|
-
}
|
|
18
|
-
return await response.json();
|
|
19
|
-
}
|
|
20
7
|
function computeTimeMin(boundary) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const match = /^P(\d+)D$/i.exec(boundary.maxAge);
|
|
24
|
-
if (match == null) return undefined;
|
|
25
|
-
const rawDays = match[1];
|
|
26
|
-
if (rawDays == null) return undefined;
|
|
27
|
-
const days = Number.parseInt(rawDays, 10);
|
|
8
|
+
const days = parseMaxAgeDays(boundary);
|
|
9
|
+
if (days == null) return undefined;
|
|
28
10
|
const date = new Date();
|
|
29
11
|
date.setDate(date.getDate() - days);
|
|
30
12
|
return date.toISOString();
|
|
@@ -37,7 +19,9 @@ async function* fetchAllCalendars(params) {
|
|
|
37
19
|
syncToken: isFirstPage ? params.syncToken : undefined,
|
|
38
20
|
pageToken
|
|
39
21
|
});
|
|
40
|
-
const data = await
|
|
22
|
+
const data = await params.client.get(url, {
|
|
23
|
+
signal: params.signal
|
|
24
|
+
});
|
|
41
25
|
yield {
|
|
42
26
|
items: data.items ?? [],
|
|
43
27
|
nextSyncToken: data.nextSyncToken,
|
|
@@ -57,7 +41,9 @@ async function* fetchEventsForCalendar(params) {
|
|
|
57
41
|
pageToken,
|
|
58
42
|
timeMin: params.timeMin
|
|
59
43
|
});
|
|
60
|
-
const data = await
|
|
44
|
+
const data = await params.client.get(url, {
|
|
45
|
+
signal: params.signal
|
|
46
|
+
});
|
|
61
47
|
yield {
|
|
62
48
|
items: data.items ?? [],
|
|
63
49
|
nextSyncToken: data.nextSyncToken
|
|
@@ -68,18 +54,13 @@ async function* fetchEventsForCalendar(params) {
|
|
|
68
54
|
}
|
|
69
55
|
export function createGoogleCalendarProvider(config) {
|
|
70
56
|
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
57
|
+
const client = calendarAPIClient(config.credential.accessToken, fetchFn);
|
|
71
58
|
const excludeSet = new Set(config.excludeCalendars ?? []);
|
|
72
59
|
const mintFetchHLC = config.mintFetchHLC;
|
|
73
60
|
// Derived once per provider instance — a pure function of (iss,sub), so it is
|
|
74
61
|
// stable across refresh and re-auth. Absent when the credential predates
|
|
75
62
|
// providerAccount capture.
|
|
76
63
|
const accountKey = config.credential.providerAccount != null ? deriveAccountKey(config.credential.providerAccount) : undefined;
|
|
77
|
-
// getCalendarListEntry/getEvent/getEventWithETag (api.ts) throw a plain Error tagged
|
|
78
|
-
// with `.httpStatus` (googleAPIError) — this is the seam fetchEntity/reconcile
|
|
79
|
-
// use to turn a 404 into a `present: false` result instead of a throw.
|
|
80
|
-
function isNotFound(err) {
|
|
81
|
-
return err?.httpStatus === 404;
|
|
82
|
-
}
|
|
83
64
|
async function* fetchAllEntities(params) {
|
|
84
65
|
// Reuse a window frozen by a prior sync (carried in an expired checkpoint on
|
|
85
66
|
// the 410 resync path, or a re-driven initial) instead of recomputing from
|
|
@@ -91,8 +72,7 @@ export function createGoogleCalendarProvider(config) {
|
|
|
91
72
|
const allCalendars = [];
|
|
92
73
|
let calendarListSyncToken;
|
|
93
74
|
for await (const page of fetchAllCalendars({
|
|
94
|
-
|
|
95
|
-
fetchFn,
|
|
75
|
+
client,
|
|
96
76
|
signal: params.signal
|
|
97
77
|
})){
|
|
98
78
|
for (const cal of page.items){
|
|
@@ -123,8 +103,7 @@ export function createGoogleCalendarProvider(config) {
|
|
|
123
103
|
let eventsSyncToken;
|
|
124
104
|
for await (const page of fetchEventsForCalendar({
|
|
125
105
|
calendarID: calendar.id,
|
|
126
|
-
|
|
127
|
-
fetchFn,
|
|
106
|
+
client,
|
|
128
107
|
timeMin,
|
|
129
108
|
signal: params.signal
|
|
130
109
|
})){
|
|
@@ -198,8 +177,7 @@ export function createGoogleCalendarProvider(config) {
|
|
|
198
177
|
const deletedCalendarIDs = [];
|
|
199
178
|
let newCalendarListSyncToken;
|
|
200
179
|
for await (const page of fetchAllCalendars({
|
|
201
|
-
|
|
202
|
-
fetchFn,
|
|
180
|
+
client,
|
|
203
181
|
syncToken: checkpoint.calendarListSyncToken,
|
|
204
182
|
signal: params.signal
|
|
205
183
|
})){
|
|
@@ -263,8 +241,7 @@ export function createGoogleCalendarProvider(config) {
|
|
|
263
241
|
let eventsSyncToken;
|
|
264
242
|
for await (const page of fetchEventsForCalendar({
|
|
265
243
|
calendarID,
|
|
266
|
-
|
|
267
|
-
fetchFn,
|
|
244
|
+
client,
|
|
268
245
|
syncToken: storedToken,
|
|
269
246
|
timeMin: isNew ? timeMin : undefined,
|
|
270
247
|
signal: params.signal
|
|
@@ -335,41 +312,32 @@ export function createGoogleCalendarProvider(config) {
|
|
|
335
312
|
// the latter's sourceID is the composite calendarID/eventID.
|
|
336
313
|
async function fetchEntity(model, sourceID) {
|
|
337
314
|
const sourceFetchHLC = mintFetchHLC();
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
315
|
+
return fetchOrAbsent({
|
|
316
|
+
sourceID,
|
|
317
|
+
sourceFetchHLC,
|
|
318
|
+
read: async ()=>{
|
|
319
|
+
if (model === CALENDAR_MODEL_ID) {
|
|
320
|
+
const calendar = await getCalendarListEntry({
|
|
321
|
+
accessToken: config.credential.accessToken,
|
|
322
|
+
calendarID: sourceID,
|
|
323
|
+
fetch: fetchFn
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
record: mapCalendarToEntity(calendar, accountKey)
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const { calendarID, eventID } = splitEventSourceID(sourceID);
|
|
330
|
+
const record = await getEvent({
|
|
341
331
|
accessToken: config.credential.accessToken,
|
|
342
|
-
calendarID
|
|
332
|
+
calendarID,
|
|
333
|
+
eventID,
|
|
343
334
|
fetch: fetchFn
|
|
344
335
|
});
|
|
345
336
|
return {
|
|
346
|
-
|
|
347
|
-
record: mapCalendarToEntity(calendar, accountKey),
|
|
348
|
-
sourceFetchHLC
|
|
337
|
+
record
|
|
349
338
|
};
|
|
350
339
|
}
|
|
351
|
-
|
|
352
|
-
const record = await getEvent({
|
|
353
|
-
accessToken: config.credential.accessToken,
|
|
354
|
-
calendarID,
|
|
355
|
-
eventID,
|
|
356
|
-
fetch: fetchFn
|
|
357
|
-
});
|
|
358
|
-
return {
|
|
359
|
-
present: true,
|
|
360
|
-
record,
|
|
361
|
-
sourceFetchHLC
|
|
362
|
-
};
|
|
363
|
-
} catch (err) {
|
|
364
|
-
if (isNotFound(err)) {
|
|
365
|
-
return {
|
|
366
|
-
present: false,
|
|
367
|
-
sourceID,
|
|
368
|
-
sourceFetchHLC
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
throw err;
|
|
372
|
-
}
|
|
340
|
+
});
|
|
373
341
|
}
|
|
374
342
|
// Provider-authoritative re-read for write-back recovery. Unlike
|
|
375
343
|
// fetchEntity, the present:true branch also surfaces a providerETag when the
|
|
@@ -377,44 +345,33 @@ export function createGoogleCalendarProvider(config) {
|
|
|
377
345
|
// condition on.
|
|
378
346
|
async function reconcile(model, sourceID) {
|
|
379
347
|
const sourceFetchHLC = mintFetchHLC();
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
348
|
+
return fetchOrAbsent({
|
|
349
|
+
sourceID,
|
|
350
|
+
sourceFetchHLC,
|
|
351
|
+
read: async ()=>{
|
|
352
|
+
if (model === CALENDAR_MODEL_ID) {
|
|
353
|
+
const calendar = await getCalendarListEntry({
|
|
354
|
+
accessToken: config.credential.accessToken,
|
|
355
|
+
calendarID: sourceID,
|
|
356
|
+
fetch: fetchFn
|
|
357
|
+
});
|
|
358
|
+
return {
|
|
359
|
+
record: mapCalendarToEntity(calendar, accountKey)
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const { calendarID, eventID } = splitEventSourceID(sourceID);
|
|
363
|
+
const { record, etag } = await getEventWithETag({
|
|
383
364
|
accessToken: config.credential.accessToken,
|
|
384
|
-
calendarID
|
|
365
|
+
calendarID,
|
|
366
|
+
eventID,
|
|
385
367
|
fetch: fetchFn
|
|
386
368
|
});
|
|
387
369
|
return {
|
|
388
|
-
|
|
389
|
-
record: mapCalendarToEntity(calendar, accountKey),
|
|
390
|
-
sourceFetchHLC
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
const { calendarID, eventID } = splitEventSourceID(sourceID);
|
|
394
|
-
const { record, etag } = await getEventWithETag({
|
|
395
|
-
accessToken: config.credential.accessToken,
|
|
396
|
-
calendarID,
|
|
397
|
-
eventID,
|
|
398
|
-
fetch: fetchFn
|
|
399
|
-
});
|
|
400
|
-
return {
|
|
401
|
-
present: true,
|
|
402
|
-
record,
|
|
403
|
-
sourceFetchHLC,
|
|
404
|
-
...etag != null ? {
|
|
370
|
+
record,
|
|
405
371
|
providerETag: etag
|
|
406
|
-
} : {}
|
|
407
|
-
};
|
|
408
|
-
} catch (err) {
|
|
409
|
-
if (isNotFound(err)) {
|
|
410
|
-
return {
|
|
411
|
-
present: false,
|
|
412
|
-
sourceID,
|
|
413
|
-
sourceFetchHLC
|
|
414
372
|
};
|
|
415
373
|
}
|
|
416
|
-
|
|
417
|
-
}
|
|
374
|
+
});
|
|
418
375
|
}
|
|
419
376
|
return {
|
|
420
377
|
supportedStrategies: [
|
|
@@ -434,7 +391,7 @@ export function createGoogleCalendarProvider(config) {
|
|
|
434
391
|
} catch (err) {
|
|
435
392
|
// 410 Gone = the stored syncToken has expired; Google requires a full
|
|
436
393
|
// resync. Discard the checkpoint by delegating to fetchAll.
|
|
437
|
-
if (err instanceof
|
|
394
|
+
if (err instanceof ConnectorError && err.httpStatus === 410) {
|
|
438
395
|
yield* fetchAllEntities(params);
|
|
439
396
|
return;
|
|
440
397
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/connector-google-calendar",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"LICENSE.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@kubun/connector": "^0.17.
|
|
18
|
+
"@kubun/connector": "^0.17.3",
|
|
19
19
|
"@kubun/credential-types": "^0.17.2",
|
|
20
20
|
"@kubun/hlc": "^0.17.0",
|
|
21
21
|
"@kubun/id": "^0.17.0",
|
|
22
22
|
"@kubun/models": "^0.17.0",
|
|
23
|
-
"@kubun/protocol": "^0.17.
|
|
24
|
-
"@kubun/provider-google": "^0.17.
|
|
23
|
+
"@kubun/protocol": "^0.17.2",
|
|
24
|
+
"@kubun/provider-google": "^0.17.1",
|
|
25
25
|
"@kubun/scalars": "^0.17.0",
|
|
26
26
|
"@sozai/codec": "^0.4.0"
|
|
27
27
|
},
|