@kubun/connector-google-mail 0.12.0
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/LICENSE.md +57 -0
- package/lib/actions.d.ts +9 -0
- package/lib/actions.js +43 -0
- package/lib/api.d.ts +125 -0
- package/lib/api.js +313 -0
- package/lib/connector.d.ts +6 -0
- package/lib/connector.js +133 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/model.d.ts +227 -0
- package/lib/model.js +245 -0
- package/lib/provider.d.ts +12 -0
- package/lib/provider.js +190 -0
- package/package.json +37 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createGoogleMailActionHandler, type GoogleMailActionHandlerConfig } from './actions.js';
|
|
2
|
+
export { buildHistoryListURL, buildLabelsListURL, buildMessageGetURL, buildMessagesListURL, buildProfileURL, buildRawMessage, createDraft, createLabel, decodeBase64Url, encodeBase64Url, extractEmail, extractEmails, type GmailHistoryListResponse, type GmailHistoryRecord, type GmailLabelResource, type GmailLabelsListResponse, type GmailMessagePart, type GmailMessageResource, type GmailMessagesListResponse, getHeader, mapLabelToEntity, mapMessageToEntity, mapThreadToEntity, modifyLabels, sendMessage, updateLabel, } from './api.js';
|
|
3
|
+
export { type CreateGoogleMailConnectorOptions, createGoogleMailConnector } from './connector.js';
|
|
4
|
+
export { googleMailClusters, googleMailLabelDefinition, googleMailMessageDefinition, googleMailThreadDefinition, } from './model.js';
|
|
5
|
+
export { createGoogleMailProvider, DEFAULT_QUERY, type GoogleMailProviderConfig, type MailCheckpoint, } from './provider.js';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createGoogleMailActionHandler } from './actions.js';
|
|
2
|
+
export { buildHistoryListURL, buildLabelsListURL, buildMessageGetURL, buildMessagesListURL, buildProfileURL, buildRawMessage, createDraft, createLabel, decodeBase64Url, encodeBase64Url, extractEmail, extractEmails, getHeader, mapLabelToEntity, mapMessageToEntity, mapThreadToEntity, modifyLabels, sendMessage, updateLabel } from './api.js';
|
|
3
|
+
export { createGoogleMailConnector } from './connector.js';
|
|
4
|
+
export { googleMailClusters, googleMailLabelDefinition, googleMailMessageDefinition, googleMailThreadDefinition } from './model.js';
|
|
5
|
+
export { createGoogleMailProvider, DEFAULT_QUERY } from './provider.js';
|
package/lib/model.d.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
export declare const googleMailThreadDefinition: {
|
|
2
|
+
readonly name: "GoogleMailThread";
|
|
3
|
+
readonly behavior: "default";
|
|
4
|
+
readonly interfaces: [string, string];
|
|
5
|
+
readonly schema: {
|
|
6
|
+
readonly type: "object";
|
|
7
|
+
readonly properties: {
|
|
8
|
+
readonly title: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
readonly maxLength: 200;
|
|
11
|
+
};
|
|
12
|
+
readonly sourceService: {
|
|
13
|
+
readonly $ref: "#/definitions/SourceService";
|
|
14
|
+
};
|
|
15
|
+
readonly sourceID: {
|
|
16
|
+
readonly $ref: "#/definitions/SourceID";
|
|
17
|
+
};
|
|
18
|
+
readonly sourceURL: {
|
|
19
|
+
readonly $ref: "#/definitions/SourceURL";
|
|
20
|
+
};
|
|
21
|
+
readonly lastSyncedAt: {
|
|
22
|
+
readonly $ref: "#/definitions/DateTime";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
readonly required: ["sourceService", "sourceID", "lastSyncedAt"];
|
|
26
|
+
readonly additionalProperties: false;
|
|
27
|
+
readonly definitions: {
|
|
28
|
+
readonly SourceService: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly title: "SourceService";
|
|
31
|
+
readonly minLength: 1;
|
|
32
|
+
readonly maxLength: 50;
|
|
33
|
+
};
|
|
34
|
+
readonly SourceID: {
|
|
35
|
+
readonly type: "string";
|
|
36
|
+
readonly title: "SourceID";
|
|
37
|
+
readonly minLength: 1;
|
|
38
|
+
readonly maxLength: 500;
|
|
39
|
+
};
|
|
40
|
+
readonly SourceURL: import("json-schema-typed").JSONSchema.String;
|
|
41
|
+
readonly DateTime: import("json-schema-typed").JSONSchema.String;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
readonly fieldsMeta: {};
|
|
45
|
+
};
|
|
46
|
+
export declare const googleMailMessageDefinition: {
|
|
47
|
+
readonly name: "GoogleMailMessage";
|
|
48
|
+
readonly behavior: "default";
|
|
49
|
+
readonly interfaces: [string, string];
|
|
50
|
+
readonly schema: {
|
|
51
|
+
readonly type: "object";
|
|
52
|
+
readonly properties: {
|
|
53
|
+
readonly body: {
|
|
54
|
+
readonly type: "string";
|
|
55
|
+
};
|
|
56
|
+
readonly sender: {
|
|
57
|
+
readonly type: "string";
|
|
58
|
+
readonly minLength: 1;
|
|
59
|
+
readonly maxLength: 320;
|
|
60
|
+
};
|
|
61
|
+
readonly subject: {
|
|
62
|
+
readonly type: "string";
|
|
63
|
+
};
|
|
64
|
+
readonly snippet: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
};
|
|
67
|
+
readonly to: {
|
|
68
|
+
readonly type: "array";
|
|
69
|
+
readonly title: "To";
|
|
70
|
+
readonly items: {
|
|
71
|
+
readonly type: "string";
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly cc: {
|
|
75
|
+
readonly type: "array";
|
|
76
|
+
readonly title: "Cc";
|
|
77
|
+
readonly items: {
|
|
78
|
+
readonly type: "string";
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
readonly labels: {
|
|
82
|
+
readonly type: "array";
|
|
83
|
+
readonly title: "Labels";
|
|
84
|
+
readonly items: {
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
readonly sentAt: {
|
|
89
|
+
readonly $ref: "#/definitions/DateTime";
|
|
90
|
+
};
|
|
91
|
+
readonly unread: {
|
|
92
|
+
readonly type: "boolean";
|
|
93
|
+
};
|
|
94
|
+
readonly attachments: {
|
|
95
|
+
readonly type: "array";
|
|
96
|
+
readonly title: "Attachments";
|
|
97
|
+
readonly items: {
|
|
98
|
+
readonly $ref: "#/definitions/Attachment";
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
readonly threadID: {
|
|
102
|
+
readonly type: "string";
|
|
103
|
+
};
|
|
104
|
+
readonly thread: {
|
|
105
|
+
readonly $ref: "#/definitions/ThreadDocID";
|
|
106
|
+
};
|
|
107
|
+
readonly sourceService: {
|
|
108
|
+
readonly $ref: "#/definitions/SourceService";
|
|
109
|
+
};
|
|
110
|
+
readonly sourceID: {
|
|
111
|
+
readonly $ref: "#/definitions/SourceID";
|
|
112
|
+
};
|
|
113
|
+
readonly sourceURL: {
|
|
114
|
+
readonly $ref: "#/definitions/SourceURL";
|
|
115
|
+
};
|
|
116
|
+
readonly lastSyncedAt: {
|
|
117
|
+
readonly $ref: "#/definitions/DateTime";
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
readonly required: ["body", "sender", "threadID", "thread", "sourceService", "sourceID", "lastSyncedAt"];
|
|
121
|
+
readonly additionalProperties: false;
|
|
122
|
+
readonly definitions: {
|
|
123
|
+
readonly ThreadDocID: import("json-schema-typed").JSONSchema.String;
|
|
124
|
+
readonly Attachment: {
|
|
125
|
+
readonly type: "object";
|
|
126
|
+
readonly title: "Attachment";
|
|
127
|
+
readonly properties: {
|
|
128
|
+
readonly filename: {
|
|
129
|
+
readonly type: "string";
|
|
130
|
+
};
|
|
131
|
+
readonly mimeType: {
|
|
132
|
+
readonly type: "string";
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
readonly required: ["filename", "mimeType"];
|
|
136
|
+
readonly additionalProperties: false;
|
|
137
|
+
};
|
|
138
|
+
readonly SourceService: {
|
|
139
|
+
readonly type: "string";
|
|
140
|
+
readonly title: "SourceService";
|
|
141
|
+
readonly minLength: 1;
|
|
142
|
+
readonly maxLength: 50;
|
|
143
|
+
};
|
|
144
|
+
readonly SourceID: {
|
|
145
|
+
readonly type: "string";
|
|
146
|
+
readonly title: "SourceID";
|
|
147
|
+
readonly minLength: 1;
|
|
148
|
+
readonly maxLength: 500;
|
|
149
|
+
};
|
|
150
|
+
readonly SourceURL: import("json-schema-typed").JSONSchema.String;
|
|
151
|
+
readonly DateTime: import("json-schema-typed").JSONSchema.String;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
readonly fieldsMeta: {
|
|
155
|
+
readonly thread: {
|
|
156
|
+
readonly type: "document";
|
|
157
|
+
readonly model: "#0";
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
export declare const googleMailLabelDefinition: {
|
|
162
|
+
readonly name: "GoogleMailLabel";
|
|
163
|
+
readonly behavior: "default";
|
|
164
|
+
readonly interfaces: [string];
|
|
165
|
+
readonly schema: {
|
|
166
|
+
readonly type: "object";
|
|
167
|
+
readonly properties: {
|
|
168
|
+
readonly name: {
|
|
169
|
+
readonly type: "string";
|
|
170
|
+
readonly minLength: 1;
|
|
171
|
+
readonly maxLength: 225;
|
|
172
|
+
};
|
|
173
|
+
readonly type: {
|
|
174
|
+
readonly type: "string";
|
|
175
|
+
readonly maxLength: 20;
|
|
176
|
+
};
|
|
177
|
+
readonly messageListVisibility: {
|
|
178
|
+
readonly type: "string";
|
|
179
|
+
readonly maxLength: 20;
|
|
180
|
+
};
|
|
181
|
+
readonly labelListVisibility: {
|
|
182
|
+
readonly type: "string";
|
|
183
|
+
readonly maxLength: 30;
|
|
184
|
+
};
|
|
185
|
+
readonly textColor: {
|
|
186
|
+
readonly type: "string";
|
|
187
|
+
readonly maxLength: 20;
|
|
188
|
+
};
|
|
189
|
+
readonly backgroundColor: {
|
|
190
|
+
readonly type: "string";
|
|
191
|
+
readonly maxLength: 20;
|
|
192
|
+
};
|
|
193
|
+
readonly sourceService: {
|
|
194
|
+
readonly $ref: "#/definitions/SourceService";
|
|
195
|
+
};
|
|
196
|
+
readonly sourceID: {
|
|
197
|
+
readonly $ref: "#/definitions/SourceID";
|
|
198
|
+
};
|
|
199
|
+
readonly sourceURL: {
|
|
200
|
+
readonly $ref: "#/definitions/SourceURL";
|
|
201
|
+
};
|
|
202
|
+
readonly lastSyncedAt: {
|
|
203
|
+
readonly $ref: "#/definitions/DateTime";
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
readonly required: ["name", "sourceService", "sourceID", "lastSyncedAt"];
|
|
207
|
+
readonly additionalProperties: false;
|
|
208
|
+
readonly definitions: {
|
|
209
|
+
readonly SourceService: {
|
|
210
|
+
readonly type: "string";
|
|
211
|
+
readonly title: "SourceService";
|
|
212
|
+
readonly minLength: 1;
|
|
213
|
+
readonly maxLength: 50;
|
|
214
|
+
};
|
|
215
|
+
readonly SourceID: {
|
|
216
|
+
readonly type: "string";
|
|
217
|
+
readonly title: "SourceID";
|
|
218
|
+
readonly minLength: 1;
|
|
219
|
+
readonly maxLength: 500;
|
|
220
|
+
};
|
|
221
|
+
readonly SourceURL: import("json-schema-typed").JSONSchema.String;
|
|
222
|
+
readonly DateTime: import("json-schema-typed").JSONSchema.String;
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
readonly fieldsMeta: {};
|
|
226
|
+
};
|
|
227
|
+
export declare const googleMailClusters: import("@kubun/protocol").ClustersRecord;
|
package/lib/model.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { connectorClusters, connectorModelIDs } from '@kubun/connector';
|
|
2
|
+
import { modelIDs, modelsClusters } from '@kubun/models';
|
|
3
|
+
import { GraphBuilder } from '@kubun/protocol';
|
|
4
|
+
import { dateTimeSchema, docIDSchema, urlSchema } from '@kubun/scalars';
|
|
5
|
+
const commonDefinitions = {
|
|
6
|
+
SourceService: {
|
|
7
|
+
type: 'string',
|
|
8
|
+
title: 'SourceService',
|
|
9
|
+
minLength: 1,
|
|
10
|
+
maxLength: 50
|
|
11
|
+
},
|
|
12
|
+
SourceID: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
title: 'SourceID',
|
|
15
|
+
minLength: 1,
|
|
16
|
+
maxLength: 500
|
|
17
|
+
},
|
|
18
|
+
SourceURL: urlSchema,
|
|
19
|
+
DateTime: dateTimeSchema
|
|
20
|
+
};
|
|
21
|
+
export const googleMailThreadDefinition = {
|
|
22
|
+
name: 'GoogleMailThread',
|
|
23
|
+
behavior: 'default',
|
|
24
|
+
interfaces: [
|
|
25
|
+
connectorModelIDs.ExternalEntity,
|
|
26
|
+
modelIDs.ThreadKind
|
|
27
|
+
],
|
|
28
|
+
schema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
title: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
maxLength: 200
|
|
34
|
+
},
|
|
35
|
+
sourceService: {
|
|
36
|
+
$ref: '#/definitions/SourceService'
|
|
37
|
+
},
|
|
38
|
+
sourceID: {
|
|
39
|
+
$ref: '#/definitions/SourceID'
|
|
40
|
+
},
|
|
41
|
+
sourceURL: {
|
|
42
|
+
$ref: '#/definitions/SourceURL'
|
|
43
|
+
},
|
|
44
|
+
lastSyncedAt: {
|
|
45
|
+
$ref: '#/definitions/DateTime'
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
required: [
|
|
49
|
+
'sourceService',
|
|
50
|
+
'sourceID',
|
|
51
|
+
'lastSyncedAt'
|
|
52
|
+
],
|
|
53
|
+
additionalProperties: false,
|
|
54
|
+
definitions: {
|
|
55
|
+
...commonDefinitions
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
fieldsMeta: {}
|
|
59
|
+
};
|
|
60
|
+
export const googleMailMessageDefinition = {
|
|
61
|
+
name: 'GoogleMailMessage',
|
|
62
|
+
behavior: 'default',
|
|
63
|
+
interfaces: [
|
|
64
|
+
connectorModelIDs.ExternalEntity,
|
|
65
|
+
modelIDs.MessageKind
|
|
66
|
+
],
|
|
67
|
+
schema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
body: {
|
|
71
|
+
type: 'string'
|
|
72
|
+
},
|
|
73
|
+
sender: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
minLength: 1,
|
|
76
|
+
maxLength: 320
|
|
77
|
+
},
|
|
78
|
+
subject: {
|
|
79
|
+
type: 'string'
|
|
80
|
+
},
|
|
81
|
+
snippet: {
|
|
82
|
+
type: 'string'
|
|
83
|
+
},
|
|
84
|
+
to: {
|
|
85
|
+
type: 'array',
|
|
86
|
+
title: 'To',
|
|
87
|
+
items: {
|
|
88
|
+
type: 'string'
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
cc: {
|
|
92
|
+
type: 'array',
|
|
93
|
+
title: 'Cc',
|
|
94
|
+
items: {
|
|
95
|
+
type: 'string'
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
labels: {
|
|
99
|
+
type: 'array',
|
|
100
|
+
title: 'Labels',
|
|
101
|
+
items: {
|
|
102
|
+
type: 'string'
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
sentAt: {
|
|
106
|
+
$ref: '#/definitions/DateTime'
|
|
107
|
+
},
|
|
108
|
+
unread: {
|
|
109
|
+
type: 'boolean'
|
|
110
|
+
},
|
|
111
|
+
attachments: {
|
|
112
|
+
type: 'array',
|
|
113
|
+
title: 'Attachments',
|
|
114
|
+
items: {
|
|
115
|
+
$ref: '#/definitions/Attachment'
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
threadID: {
|
|
119
|
+
type: 'string'
|
|
120
|
+
},
|
|
121
|
+
thread: {
|
|
122
|
+
$ref: '#/definitions/ThreadDocID'
|
|
123
|
+
},
|
|
124
|
+
sourceService: {
|
|
125
|
+
$ref: '#/definitions/SourceService'
|
|
126
|
+
},
|
|
127
|
+
sourceID: {
|
|
128
|
+
$ref: '#/definitions/SourceID'
|
|
129
|
+
},
|
|
130
|
+
sourceURL: {
|
|
131
|
+
$ref: '#/definitions/SourceURL'
|
|
132
|
+
},
|
|
133
|
+
lastSyncedAt: {
|
|
134
|
+
$ref: '#/definitions/DateTime'
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
required: [
|
|
138
|
+
'body',
|
|
139
|
+
'sender',
|
|
140
|
+
'threadID',
|
|
141
|
+
'thread',
|
|
142
|
+
'sourceService',
|
|
143
|
+
'sourceID',
|
|
144
|
+
'lastSyncedAt'
|
|
145
|
+
],
|
|
146
|
+
additionalProperties: false,
|
|
147
|
+
definitions: {
|
|
148
|
+
...commonDefinitions,
|
|
149
|
+
ThreadDocID: docIDSchema,
|
|
150
|
+
Attachment: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
title: 'Attachment',
|
|
153
|
+
properties: {
|
|
154
|
+
filename: {
|
|
155
|
+
type: 'string'
|
|
156
|
+
},
|
|
157
|
+
mimeType: {
|
|
158
|
+
type: 'string'
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
required: [
|
|
162
|
+
'filename',
|
|
163
|
+
'mimeType'
|
|
164
|
+
],
|
|
165
|
+
additionalProperties: false
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
fieldsMeta: {
|
|
170
|
+
thread: {
|
|
171
|
+
type: 'document',
|
|
172
|
+
model: '#0'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
export const googleMailLabelDefinition = {
|
|
177
|
+
name: 'GoogleMailLabel',
|
|
178
|
+
behavior: 'default',
|
|
179
|
+
interfaces: [
|
|
180
|
+
connectorModelIDs.ExternalEntity
|
|
181
|
+
],
|
|
182
|
+
schema: {
|
|
183
|
+
type: 'object',
|
|
184
|
+
properties: {
|
|
185
|
+
name: {
|
|
186
|
+
type: 'string',
|
|
187
|
+
minLength: 1,
|
|
188
|
+
maxLength: 225
|
|
189
|
+
},
|
|
190
|
+
type: {
|
|
191
|
+
type: 'string',
|
|
192
|
+
maxLength: 20
|
|
193
|
+
},
|
|
194
|
+
messageListVisibility: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
maxLength: 20
|
|
197
|
+
},
|
|
198
|
+
labelListVisibility: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
maxLength: 30
|
|
201
|
+
},
|
|
202
|
+
textColor: {
|
|
203
|
+
type: 'string',
|
|
204
|
+
maxLength: 20
|
|
205
|
+
},
|
|
206
|
+
backgroundColor: {
|
|
207
|
+
type: 'string',
|
|
208
|
+
maxLength: 20
|
|
209
|
+
},
|
|
210
|
+
sourceService: {
|
|
211
|
+
$ref: '#/definitions/SourceService'
|
|
212
|
+
},
|
|
213
|
+
sourceID: {
|
|
214
|
+
$ref: '#/definitions/SourceID'
|
|
215
|
+
},
|
|
216
|
+
sourceURL: {
|
|
217
|
+
$ref: '#/definitions/SourceURL'
|
|
218
|
+
},
|
|
219
|
+
lastSyncedAt: {
|
|
220
|
+
$ref: '#/definitions/DateTime'
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
required: [
|
|
224
|
+
'name',
|
|
225
|
+
'sourceService',
|
|
226
|
+
'sourceID',
|
|
227
|
+
'lastSyncedAt'
|
|
228
|
+
],
|
|
229
|
+
additionalProperties: false,
|
|
230
|
+
definitions: {
|
|
231
|
+
...commonDefinitions
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
fieldsMeta: {}
|
|
235
|
+
};
|
|
236
|
+
const builder = new GraphBuilder({
|
|
237
|
+
externalClusters: {
|
|
238
|
+
...connectorClusters,
|
|
239
|
+
...modelsClusters
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
builder.add(googleMailThreadDefinition); // #0
|
|
243
|
+
builder.add(googleMailMessageDefinition); // #1
|
|
244
|
+
builder.add(googleMailLabelDefinition); // #2
|
|
245
|
+
export const googleMailClusters = builder.build();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Credential, ServerDataProvider, SyncBoundary } from '@kubun/connector';
|
|
2
|
+
export declare const DEFAULT_QUERY = "in:inbox";
|
|
3
|
+
export type MailCheckpoint = {
|
|
4
|
+
historyId: string;
|
|
5
|
+
};
|
|
6
|
+
export type GoogleMailProviderConfig = {
|
|
7
|
+
credential: Credential;
|
|
8
|
+
boundary?: SyncBoundary;
|
|
9
|
+
fetch?: typeof globalThis.fetch;
|
|
10
|
+
query?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function createGoogleMailProvider(config: GoogleMailProviderConfig): ServerDataProvider;
|
package/lib/provider.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { resolveModelID } from '@kubun/connector';
|
|
2
|
+
import { buildHistoryListURL, buildLabelsListURL, buildMessageGetURL, buildMessagesListURL, buildProfileURL, getHeader, mapLabelToEntity, mapMessageToEntity, mapThreadToEntity, parseGoogleError } from './api.js';
|
|
3
|
+
import { googleMailClusters } from './model.js';
|
|
4
|
+
export const DEFAULT_QUERY = 'in:inbox';
|
|
5
|
+
const THREAD_MODEL_ID = resolveModelID(googleMailClusters, 'GoogleMailThread');
|
|
6
|
+
const MESSAGE_MODEL_ID = resolveModelID(googleMailClusters, 'GoogleMailMessage');
|
|
7
|
+
const LABEL_MODEL_ID = resolveModelID(googleMailClusters, 'GoogleMailLabel');
|
|
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 Error(`Google Mail API error: ${await parseGoogleError(response)}`);
|
|
17
|
+
}
|
|
18
|
+
return await response.json();
|
|
19
|
+
}
|
|
20
|
+
function applyMaxAge(query, boundary) {
|
|
21
|
+
if (boundary?.maxAge == null) return query;
|
|
22
|
+
const match = /^P(\d+)D$/i.exec(boundary.maxAge);
|
|
23
|
+
if (match == null) return query;
|
|
24
|
+
if (/\b(newer_than:|after:)/i.test(query)) return query;
|
|
25
|
+
return `${query} newer_than:${match[1]}d`;
|
|
26
|
+
}
|
|
27
|
+
function maxHistoryId(messages) {
|
|
28
|
+
let max = -1;
|
|
29
|
+
for (const m of messages){
|
|
30
|
+
const n = m.historyId != null ? Number.parseInt(m.historyId, 10) : Number.NaN;
|
|
31
|
+
if (!Number.isNaN(n) && n > max) max = n;
|
|
32
|
+
}
|
|
33
|
+
return max < 0 ? undefined : String(max);
|
|
34
|
+
}
|
|
35
|
+
export function createGoogleMailProvider(config) {
|
|
36
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
37
|
+
async function listMessageIDs(query, signal) {
|
|
38
|
+
const ids = [];
|
|
39
|
+
let pageToken;
|
|
40
|
+
do {
|
|
41
|
+
const data = await fetchJSON(buildMessagesListURL({
|
|
42
|
+
query,
|
|
43
|
+
pageToken
|
|
44
|
+
}), config.credential, fetchFn, signal);
|
|
45
|
+
for (const m of data.messages ?? [])ids.push(m);
|
|
46
|
+
pageToken = data.nextPageToken;
|
|
47
|
+
}while (pageToken != null)
|
|
48
|
+
return ids;
|
|
49
|
+
}
|
|
50
|
+
async function getMessage(id, signal) {
|
|
51
|
+
return fetchJSON(buildMessageGetURL({
|
|
52
|
+
messageID: id
|
|
53
|
+
}), config.credential, fetchFn, signal);
|
|
54
|
+
}
|
|
55
|
+
async function profileHistoryId(signal) {
|
|
56
|
+
const profile = await fetchJSON(buildProfileURL(), config.credential, fetchFn, signal);
|
|
57
|
+
return profile.historyId ?? '0';
|
|
58
|
+
}
|
|
59
|
+
// Labels have no incremental history API, so every sync re-lists them in full.
|
|
60
|
+
// The list endpoint is a single cheap call; it omits color (captured only on
|
|
61
|
+
// create/edit) but carries id, name, type, and the visibility fields.
|
|
62
|
+
async function labelBatch(signal) {
|
|
63
|
+
const data = await fetchJSON(buildLabelsListURL(), config.credential, fetchFn, signal);
|
|
64
|
+
return {
|
|
65
|
+
modelID: LABEL_MODEL_ID,
|
|
66
|
+
entities: (data.labels ?? []).map(mapLabelToEntity),
|
|
67
|
+
hasMore: true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function* fetchAllEntities(params) {
|
|
71
|
+
yield await labelBatch(params.signal);
|
|
72
|
+
const query = applyMaxAge(config.query ?? DEFAULT_QUERY, params.boundary);
|
|
73
|
+
const ids = await listMessageIDs(query, params.signal);
|
|
74
|
+
const messages = [];
|
|
75
|
+
for (const { id } of ids){
|
|
76
|
+
messages.push(await getMessage(id, params.signal));
|
|
77
|
+
}
|
|
78
|
+
// Thread batch: one thread per distinct threadId, titled by its first message's subject.
|
|
79
|
+
const threadSubjects = new Map();
|
|
80
|
+
for (const m of messages){
|
|
81
|
+
if (!threadSubjects.has(m.threadId)) {
|
|
82
|
+
threadSubjects.set(m.threadId, getHeader(m.payload?.headers, 'Subject'));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
yield {
|
|
86
|
+
modelID: THREAD_MODEL_ID,
|
|
87
|
+
entities: [
|
|
88
|
+
...threadSubjects
|
|
89
|
+
].map(([threadId, subject])=>mapThreadToEntity(threadId, subject)),
|
|
90
|
+
hasMore: true
|
|
91
|
+
};
|
|
92
|
+
const checkpoint = {
|
|
93
|
+
historyId: maxHistoryId(messages) ?? await profileHistoryId(params.signal)
|
|
94
|
+
};
|
|
95
|
+
yield {
|
|
96
|
+
modelID: MESSAGE_MODEL_ID,
|
|
97
|
+
entities: messages.map(mapMessageToEntity),
|
|
98
|
+
hasMore: false,
|
|
99
|
+
checkpoint
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async function* fetchChangesEntities(params) {
|
|
103
|
+
const checkpoint = params.checkpoint;
|
|
104
|
+
if (checkpoint == null) {
|
|
105
|
+
yield* fetchAllEntities(params);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
yield await labelBatch(params.signal);
|
|
109
|
+
const addedIDs = new Map() // id -> threadId
|
|
110
|
+
;
|
|
111
|
+
const deletedIDs = new Set();
|
|
112
|
+
let latestHistoryId = checkpoint.historyId;
|
|
113
|
+
let pageToken;
|
|
114
|
+
do {
|
|
115
|
+
const response = await fetchFn(buildHistoryListURL({
|
|
116
|
+
startHistoryId: checkpoint.historyId,
|
|
117
|
+
pageToken
|
|
118
|
+
}), {
|
|
119
|
+
headers: {
|
|
120
|
+
Authorization: `Bearer ${config.credential.accessToken}`
|
|
121
|
+
},
|
|
122
|
+
signal: params.signal
|
|
123
|
+
});
|
|
124
|
+
if (response.status === 404) {
|
|
125
|
+
yield* fetchAllEntities(params);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
throw new Error(`Google Mail API error: ${await parseGoogleError(response)}`);
|
|
130
|
+
}
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
for (const record of data.history ?? []){
|
|
133
|
+
for (const ref of [
|
|
134
|
+
...record.messagesAdded ?? [],
|
|
135
|
+
...record.labelsAdded ?? [],
|
|
136
|
+
...record.labelsRemoved ?? []
|
|
137
|
+
]){
|
|
138
|
+
addedIDs.set(ref.message.id, ref.message.threadId);
|
|
139
|
+
}
|
|
140
|
+
for (const ref of record.messagesDeleted ?? []){
|
|
141
|
+
deletedIDs.add(ref.message.id);
|
|
142
|
+
addedIDs.delete(ref.message.id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (data.historyId != null) latestHistoryId = data.historyId;
|
|
146
|
+
pageToken = data.nextPageToken;
|
|
147
|
+
}while (pageToken != null)
|
|
148
|
+
const survivingIDs = [
|
|
149
|
+
...addedIDs.keys()
|
|
150
|
+
].filter((id)=>!deletedIDs.has(id));
|
|
151
|
+
const messages = [];
|
|
152
|
+
for (const id of survivingIDs){
|
|
153
|
+
messages.push(await getMessage(id, params.signal));
|
|
154
|
+
}
|
|
155
|
+
const threadSubjects = new Map();
|
|
156
|
+
for (const m of messages){
|
|
157
|
+
if (!threadSubjects.has(m.threadId)) {
|
|
158
|
+
threadSubjects.set(m.threadId, getHeader(m.payload?.headers, 'Subject'));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
yield {
|
|
162
|
+
modelID: THREAD_MODEL_ID,
|
|
163
|
+
entities: [
|
|
164
|
+
...threadSubjects
|
|
165
|
+
].map(([threadId, subject])=>mapThreadToEntity(threadId, subject)),
|
|
166
|
+
hasMore: true
|
|
167
|
+
};
|
|
168
|
+
yield {
|
|
169
|
+
modelID: MESSAGE_MODEL_ID,
|
|
170
|
+
entities: messages.map(mapMessageToEntity),
|
|
171
|
+
deleted: [
|
|
172
|
+
...deletedIDs
|
|
173
|
+
].map((id)=>({
|
|
174
|
+
sourceService: 'google-mail',
|
|
175
|
+
sourceID: id
|
|
176
|
+
})),
|
|
177
|
+
hasMore: false,
|
|
178
|
+
checkpoint: {
|
|
179
|
+
historyId: latestHistoryId
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
supportedStrategies: [
|
|
185
|
+
'on-demand'
|
|
186
|
+
],
|
|
187
|
+
fetchAll: fetchAllEntities,
|
|
188
|
+
fetchChanges: fetchChangesEntities
|
|
189
|
+
};
|
|
190
|
+
}
|