Created
August 28, 2023 21:14
-
-
Save moorage/6f599f4dc85ae52c505f5e304c326935 to your computer and use it in GitHub Desktop.
How to fetch gmail in batch with javascript / typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const gmailBatchEndpoint = 'https://www.googleapis.com/batch/gmail/v1' | |
const batchLimit = 2 | |
async function fetchFullMessages( | |
auth: OAuth2Client, | |
messageIds: string[] = [] | |
) { | |
const messageQueries = messageIds.map(function (id) { | |
return { uri: '/gmail/v1/users/me/messages/' + id } | |
}) | |
const limitedMessageQueries = messageQueries.slice(0, batchLimit) | |
return await fetchBatch( | |
( | |
await auth.getAccessToken() | |
).token!, | |
limitedMessageQueries, | |
'batch_taskjet_google_lib_api', | |
gmailBatchEndpoint | |
) | |
} | |
function fetchInboxMessageIds( | |
auth: OAuth2Client, | |
messageIds: string[] = [], | |
// messageIds: gmail_v1.Schema$Message[] = [], | |
pageToken = '' | |
) { | |
return new Promise<string[]>((resolve, reject) => { | |
let args = { | |
userId: 'me', | |
labelIds: ['INBOX'] | |
} | |
if (pageToken) { | |
args = Object.assign({}, args, { pageToken }) | |
} | |
const g = gmail({ version: 'v1', auth: auth as any }) | |
g.users.messages | |
.list(args) | |
.then(res => { | |
messageIds = (messageIds || []).concat( | |
(res.data.messages || []).map(message => message.id!) | |
) | |
if (res.data.nextPageToken && pageToken !== res.data.nextPageToken) | |
return resolve( | |
fetchInboxMessageIds(auth, messageIds, res.data.nextPageToken) | |
) | |
return resolve(messageIds) | |
}) | |
.catch(err => { | |
return reject(err) | |
}) | |
}) | |
} | |
// @see https://github.com/EmilTholin/google-api-batch-utils/blob/master/lib/index.js | |
import queryString from 'query-string' | |
function parsePart(part: string) { | |
var p = part.substring(part.indexOf('{'), part.lastIndexOf('}') + 1) | |
return JSON.parse(p) | |
} | |
/** | |
* Takes an array of API call objects and generates a fetch call to Google batch API. | |
* @param {string} accessToken - Access token for the API calls. | |
* @param {object[]} apiCalls | |
* @param {string} apiCalls[].uri - Uri of the API call. | |
* @param {string} apiCalls[].[method] - Optional HTTP method. Defaults to GET. | |
* @param {object} apiCalls[].[qs] - Optional object with querystring parameters. | |
* @param {string} apiCalls[].[body] - Optional request body string. | |
* @param {string} [boundary] - Optional String that delimits the calls. | |
* @param {string} [batchUrl] - Optional URL to the batch endpoint. | |
* | |
* @return {string} | |
*/ | |
export async function fetchBatch( | |
accessToken: string, | |
apiCalls: { | |
uri: string | |
method?: string | null | undefined | |
qs?: any | null | undefined | |
body?: string | null | undefined | |
}[], | |
boundary: string = 'batch_taskjet_google_lib_api', | |
batchUrl: string = 'https://www.googleapis.com/batch' | |
) { | |
const createBatchBody = function ( | |
apiCalls: { | |
uri: string | |
method?: string | null | undefined | |
qs?: any | null | undefined | |
body?: string | null | undefined | |
}[], | |
boundary: string | |
) { | |
var batchBody: string[] = [] | |
apiCalls.forEach(function (call) { | |
var method = call.method || 'GET' | |
var uri = call.uri | |
if (call.qs) { | |
uri += '?' + queryString.stringify(call.qs) | |
} | |
var body = '\r\n' | |
if (call.body) { | |
body = [ | |
'Content-Type: application/json', | |
'\r\n\r\n', | |
JSON.stringify(call.body), | |
'\r\n' | |
].join('') | |
} | |
batchBody = batchBody.concat([ | |
'--', | |
boundary, | |
'\r\n', | |
'Content-Type: application/http', | |
'\r\n\r\n', | |
method, | |
' ', | |
uri, | |
'\r\n', | |
body | |
]) | |
}) | |
return batchBody.concat(['--', boundary, '--']).join('') | |
} | |
var batchBody = createBatchBody(apiCalls, boundary) | |
const response = await fetch(batchUrl, { | |
method: 'POST', | |
headers: { | |
Authorization: 'Bearer ' + accessToken, | |
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"' | |
}, | |
body: batchBody | |
}) | |
const text = await response.text() | |
console.log('batchBody', batchBody, 'accessToken', accessToken, 'text', text) | |
return parseBatchResponse(text) | |
} | |
/** | |
* Parses a raw string response from the Google batch API into objects. | |
* @param {string} response | |
* @return {object[]} | |
*/ | |
function parseBatchResponse(response: string): any[] { | |
// Not the same delimiter in the response as was specified in the request, | |
// so we have to extract it. | |
var delimiter = response.substring(0, response.indexOf('\r\n')) | |
var parts = response.split(delimiter) | |
// The first part will always be an empty string. Just remove it. | |
parts.shift() | |
// The last part will be the "--". Just remove it. | |
parts.pop() | |
var result = [] | |
for (var i = 0; i < parts.length; i++) { | |
var part = parts[i] | |
try { | |
result.push(parsePart(part)) | |
} catch (e) { | |
// A Google API error will contain a JSON response with an array 'errors'. | |
// If the parsing should fail, we mimic this. | |
result.push({ | |
errors: [{ message: part, response: response, parts: parts, error: e }] | |
}) | |
} | |
} | |
return result | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: