rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
349 lines (343 loc) • 12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FOLDER_MIME_TYPE = exports.DRIVE_MAX_PAGE_SIZE = exports.DRIVE_MAX_BULK_SIZE = exports.DRIVE_API_VERSION = void 0;
exports.createEmptyFile = createEmptyFile;
exports.createFolder = createFolder;
exports.createMultipartBody = createMultipartBody;
exports.deleteFile = deleteFile;
exports.deleteIfEtagMatches = deleteIfEtagMatches;
exports.ensureFolderExists = ensureFolderExists;
exports.fillFileIfEtagMatches = fillFileIfEtagMatches;
exports.findFolder = findFolder;
exports.insertMultipartFile = insertMultipartFile;
exports.listFilesInFolder = listFilesInFolder;
exports.readFolder = readFolder;
exports.readJsonFileContent = readJsonFileContent;
var _rxError = require("../../rx-error.js");
var _index = require("../utils/index.js");
var DRIVE_API_VERSION = exports.DRIVE_API_VERSION = 'v3';
var DRIVE_MAX_PAGE_SIZE = exports.DRIVE_MAX_PAGE_SIZE = 1000;
var DRIVE_MAX_BULK_SIZE = exports.DRIVE_MAX_BULK_SIZE = DRIVE_MAX_PAGE_SIZE / 4;
var FOLDER_MIME_TYPE = exports.FOLDER_MIME_TYPE = 'application/vnd.google-apps.folder';
async function createFolder(googleDriveOptions, parentId = 'root', folderName) {
var url = googleDriveOptions.apiEndpoint + '/drive/v3/files?fields=id,name,mimeType,trashed';
var body = {
name: folderName,
mimeType: FOLDER_MIME_TYPE,
parents: [parentId]
};
var response = await fetch(url, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + googleDriveOptions.authToken,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
var errorText = await response.text();
if (response.status == 409) {
// someone else created the same folder, return that one instead.
var found = await findFolder(googleDriveOptions, parentId, folderName);
return (0, _index.ensureNotFalsy)(found);
}
throw await (0, _rxError.newRxFetchError)(response, {
folderName,
parentId
});
}
await response.json();
/**
* To make the function idempotent, we do not use the id from the creation-response.
* Instead after creating the folder, we search for it again so that in case
* some other instance created the same folder, we use the oldest one always.
*/
var foundFolder = await findFolder(googleDriveOptions, parentId, folderName);
return (0, _index.ensureNotFalsy)(foundFolder);
}
async function findFolder(googleDriveOptions, parentId = 'root', folderName) {
var query = "name = '" + folderName + "' and '" + parentId + "' in parents and trashed = false and mimeType = '" + FOLDER_MIME_TYPE + "'";
/**
* We sort by createdTime ASC
* so in case the same folder was created multiple times, we always pick the same
* one which is the oldest one.
*/
var searchUrl = googleDriveOptions.apiEndpoint + '/drive/v3/files?fields=files(id,mimeType)&orderBy=createdTime asc&q=' + encodeURIComponent(query);
var searchResponse = await fetch(searchUrl, {
method: 'GET',
headers: {
Authorization: 'Bearer ' + googleDriveOptions.authToken
}
});
var searchData = await searchResponse.json();
if (searchData.files && searchData.files.length > 0) {
var file = searchData.files[0];
if (file.mimeType !== FOLDER_MIME_TYPE) {
throw (0, _rxError.newRxError)('GDR3', {
folderName,
args: {
file,
FOLDER_MIME_TYPE
}
});
}
return file.id;
} else {
return undefined;
}
}
async function ensureFolderExists(googleDriveOptions, folderPath) {
var parts = folderPath.split('/').filter(p => p.length > 0);
var parentId = 'root';
for (var part of parts) {
var newParentId = await findFolder(googleDriveOptions, parentId, part);
if (newParentId) {
parentId = newParentId;
} else {
parentId = await createFolder(googleDriveOptions, parentId, part);
}
}
return parentId;
}
async function createEmptyFile(googleDriveOptions, parentId, fileName) {
var url = googleDriveOptions.apiEndpoint + '/drive/v3/files?fields=id';
var body = {
name: fileName,
parents: [parentId],
mimeType: 'application/json'
};
var response = await fetch(url, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + googleDriveOptions.authToken,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
/**
* Do not throw on duplicates,
* if the file is there already, find its id
* and return that one.
*/
if (!response.ok && response.status !== 409) {
throw await (0, _rxError.newRxFetchError)(response, {
folderName: fileName
});
}
/**
* For idempotent runs, fetch the file again
* after creating it.
*/
var query = ["name = '" + fileName + "'", "'" + parentId + "' in parents", "trashed = false"].join(' and ');
var url2 = googleDriveOptions.apiEndpoint + '/drive/v3/files' + '?fields=files(id,etag,size,createdTime)' + '&orderBy=createdTime asc' + '&q=' + encodeURIComponent(query);
var res = await fetch(url2, {
headers: {
Authorization: 'Bearer ' + googleDriveOptions.authToken
}
});
var data = await res.json();
var file = (0, _index.ensureNotFalsy)(data.files[0]);
return {
status: response.status,
etag: (0, _index.ensureNotFalsy)(file.etag),
createdTime: (0, _index.ensureNotFalsy)(file.createdTime),
fileId: (0, _index.ensureNotFalsy)(file.id),
size: parseInt(file.size, 10)
};
}
async function fillFileIfEtagMatches(googleDriveOptions, fileId, etag, jsonContent) {
var url = "" + googleDriveOptions.apiEndpoint + ("/upload/drive/v2/files/" + encodeURIComponent(fileId)) + "?uploadType=media";
var writeContent = typeof jsonContent !== 'undefined' ? JSON.stringify(jsonContent) : '';
var res = await fetch(url, {
method: "PUT",
headers: {
Authorization: "Bearer " + googleDriveOptions.authToken,
"Content-Type": "application/json; charset=utf-8",
"If-Match": etag
},
body: writeContent
});
if (res.status !== 412 && res.status !== 200) {
throw await (0, _rxError.newRxFetchError)(res);
}
return readJsonFileContent(googleDriveOptions, fileId).then(r => {
return {
content: r.content,
etag: r.etag,
status: res.status,
serverTime: r.serverTime
};
});
}
async function deleteIfEtagMatches(googleDriveOptions, fileId, etag) {
var url = "" + googleDriveOptions.apiEndpoint + ("/drive/v2/files/" + encodeURIComponent(fileId));
var res = await fetch(url, {
method: "DELETE",
headers: {
Authorization: "Bearer " + googleDriveOptions.authToken,
"If-Match": etag
}
});
if (!res.ok) {
throw await (0, _rxError.newRxFetchError)(res, {
args: {
etag,
fileId
}
});
}
if (res.ok) {
// Drive v2 returns 204 No Content on successful delete
return;
}
}
async function deleteFile(googleDriveOptions, fileId) {
var url = "" + googleDriveOptions.apiEndpoint + ("/drive/v2/files/" + encodeURIComponent(fileId));
var res = await fetch(url, {
method: "DELETE",
headers: {
Authorization: "Bearer " + googleDriveOptions.authToken
}
});
if (!res.ok) {
throw await (0, _rxError.newRxFetchError)(res, {
args: {
fileId
}
});
}
if (res.ok) {
// Drive v2 returns 204 No Content on successful delete
return;
}
}
async function readJsonFileContent(googleDriveOptions, fileId) {
var url = "" + googleDriveOptions.apiEndpoint + ("/drive/v2/files/" + encodeURIComponent(fileId) + "?alt=media");
var res = await fetch(url, {
method: "GET",
headers: {
Authorization: "Bearer " + googleDriveOptions.authToken,
Accept: "application/json"
}
});
if (!res.ok) {
throw await (0, _rxError.newRxFetchError)(res, {
args: {
fileId
}
});
}
var dateHeader = res.headers.get('date');
var unixMs = Date.parse((0, _index.ensureNotFalsy)(dateHeader));
var contentType = res.headers.get("content-type") || "";
if (!contentType.includes("application/json")) {
var err = new Error("NOT_A_JSON_FILE but " + contentType);
err.code = "NOT_A_JSON_FILE";
err.contentType = contentType;
throw err;
}
var contentText = await res.text();
var content = contentText.length > 0 ? JSON.parse(contentText) : undefined;
var etag = (0, _index.ensureNotFalsy)(res.headers.get('etag'));
return {
etag,
content: content,
serverTime: unixMs
};
}
async function readFolder(googleDriveOptions, folderPath) {
var parentId = 'root';
var parts = folderPath.split('/').filter(p => p.length > 0);
// Resolve folder path
for (var part of parts) {
var _query = "name = '" + part + "' and '" + parentId + "' in parents and trashed = false and mimeType = '" + FOLDER_MIME_TYPE + "'";
var searchUrl = googleDriveOptions.apiEndpoint + '/drive/v3/files?fields=files(id)&q=' + encodeURIComponent(_query);
var searchResponse = await fetch(searchUrl, {
method: 'GET',
headers: {
Authorization: 'Bearer ' + googleDriveOptions.authToken
}
});
var searchData = await searchResponse.json();
if (searchData.files && searchData.files.length > 0) {
parentId = searchData.files[0].id;
} else {
throw (0, _rxError.newRxError)('SNH', {
folderPath
});
}
}
// List children
var query = "'" + parentId + "' in parents and trashed = false";
var listUrl = googleDriveOptions.apiEndpoint + '/drive/v3/files?fields=files(id,name,mimeType,trashed,parents)&q=' + encodeURIComponent(query);
var listResponse = await fetch(listUrl, {
method: 'GET',
headers: {
Authorization: 'Bearer ' + googleDriveOptions.authToken
}
});
if (!listResponse.ok) {
throw await (0, _rxError.newRxFetchError)(listResponse, {
folderName: folderPath
});
}
var listData = await listResponse.json();
return listData.files || [];
}
async function insertMultipartFile(googleDriveOptions, folderId, filename, jsonData) {
var content = JSON.stringify(jsonData);
var metadata = {
name: filename,
mimeType: 'application/json',
parents: [folderId]
};
var postData = createMultipartBody(metadata, content);
var res = await fetch(googleDriveOptions.apiEndpoint + "/upload/drive/v3/files?uploadType=multipart", {
method: 'POST',
headers: {
Authorization: "Bearer " + googleDriveOptions.authToken,
'Content-Type': 'multipart/related; boundary="' + postData.boundary + '"'
},
body: postData.body
});
if (!res.ok) {
throw await (0, _rxError.newRxFetchError)(res);
}
}
function createMultipartBody(metadata, content) {
var multipartBoundary = '-------1337-use-RxDB-7355608-' + Math.random().toString(16).slice(2);
var delimiter = '\r\n--' + multipartBoundary + '\r\n';
var closeDelim = '\r\n--' + multipartBoundary + '--';
var body = delimiter + 'Content-Type: application/json\r\n\r\n' + JSON.stringify(metadata) + delimiter + 'Content-Type: application/json\r\n\r\n' + content + closeDelim;
return {
body,
boundary: multipartBoundary
};
}
;
async function listFilesInFolder(googleDriveOptions, folderId) {
var q = "'" + folderId + "' in parents and trashed = false";
var params = new URLSearchParams({
q,
pageSize: "1000",
// max allowed
fields: "files(id,name,mimeType,parents,modifiedTime,size)",
supportsAllDrives: "true",
includeItemsFromAllDrives: "true"
});
var url = googleDriveOptions.apiEndpoint + "/drive/v3/files?" + params.toString();
var res = await fetch(url, {
headers: {
Authorization: "Bearer " + googleDriveOptions.authToken
}
});
if (!res.ok) {
var text = await res.text().catch(() => "");
throw new Error("files.list failed: " + res.status + " " + text);
}
var data = await res.json();
return data.files ?? [];
}
//# sourceMappingURL=google-drive-helper.js.map