UNPKG

react-native-cloud-storage

Version:

☁️ Save to & read from iCloud and Google Drive using React Native

218 lines (214 loc) 6.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.GoogleDriveHttpError = void 0; var _types = require("./types"); var _localFs = require("../../utils/local-fs"); const BASE_URL = 'https://www.googleapis.com/drive/v3'; const BASE_UPLOAD_URL = 'https://www.googleapis.com/upload/drive/v3'; const MULTIPART_BOUNDARY = 'foo_bar_baz'; class GoogleDriveHttpError extends Error { // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(message, status, json) { super(message); this.status = status; this.json = json; } } // TODO: properly handle errors exports.GoogleDriveHttpError = GoogleDriveHttpError; class GoogleDriveApiClient { _fetchTimeout = null; constructor(options) { this.options = options; } buildQueryString(query) { let result = Object.entries(query).filter(([, value]) => value !== undefined).map(([key, value]) => { if (typeof value === 'boolean') return `${encodeURIComponent(key)}=${value ? 'true' : 'false'}`; return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; }).join('&'); if (result) { result = `?${result}`; } return result; } async request(operation, { queryParameters, baseUrl, ...options } = {}) { const { timeout, accessToken } = this.options; let path = `${baseUrl ?? BASE_URL}${operation}`; if (queryParameters) { path += this.buildQueryString(queryParameters); } if (this._fetchTimeout !== null) clearTimeout(this._fetchTimeout); const abortController = new AbortController(); this._fetchTimeout = setTimeout(() => { abortController.abort(); }, timeout); const response = await fetch(path, { ...options, headers: { ...options.headers, Authorization: `Bearer ${accessToken}` }, signal: abortController.signal }); if (!response.ok) { let errorMessage; let json = null; try { json = await response.json(); errorMessage = typeof json === 'object' && json !== null && 'error' in json && typeof json.error === 'object' && json.error !== null && 'message' in json.error && typeof json.error.message === 'string' ? json.error.message : `Request failed with status ${response.status}`; } catch { errorMessage = `Request failed with status ${response.status}`; } throw new GoogleDriveHttpError(errorMessage, response.status, json); } if (response.status === 204) { return undefined; } if (options?.headers && 'Accept' in options.headers && options.headers.Accept !== 'application/json') { return response.text(); } return response.json(); } buildMultiPartBody(metadata, media) { const body = []; body.push(`--${MULTIPART_BOUNDARY}\r\n`, `Content-Type: ${_types.MimeTypes.JSON}; charset=UTF-8\r\n\r\n`, `${JSON.stringify(metadata)}\r\n`, `--${MULTIPART_BOUNDARY}\r\n`, `Content-Type: ${media.mimeType}\r\n\r\n`, `${media.body}\r\n`, `--${MULTIPART_BOUNDARY}--`); return body.join(''); } async listFiles(space) { const files = []; let pageToken; const fields = ['id', 'kind', 'mimeType', 'name', 'parents', 'spaces', 'size', 'createdTime', 'modifiedTime']; do { const queryParameters = { fields: `files(${fields.join(',')}),nextPageToken`, spaces: space, pageToken }; const response = await this.request(`/files`, { queryParameters }); files.push(...response.files); pageToken = response.nextPageToken ?? undefined; } while (pageToken); return files; } async getFile(fileId) { const queryParameters = { fields: ['id', 'kind', 'mimeType', 'name', 'parents', 'spaces', 'size', 'createdTime', 'modifiedTime'].join(',') }; return this.request(`/files/${fileId}`, { queryParameters }); } async getFileText(fileId) { return this.request(`/files/${fileId}`, { queryParameters: { alt: 'media' }, headers: { Accept: 'text/plain' } }); } async deleteFile(fileId) { return this.request(`/files/${fileId}`, { method: 'DELETE' }); } async createFile(metadata, media) { if ('body' in media) { const multipartRequestBody = this.buildMultiPartBody(metadata, media); await this.request(`/files`, { queryParameters: { uploadType: 'multipart' }, method: 'POST', headers: { 'Content-Type': `multipart/related; boundary=${MULTIPART_BOUNDARY}`, 'Content-Length': multipartRequestBody.length.toString() }, body: multipartRequestBody, baseUrl: BASE_UPLOAD_URL }); } else { // First, create an empty file with the required metadata const file = await this.request(`/files`, { method: 'POST', headers: { 'Content-Type': _types.MimeTypes.JSON }, body: JSON.stringify({ ...metadata, mimeType: media.mimeType }) }); // Then, upload the binary content via the native helper module const remoteUri = `${BASE_UPLOAD_URL}/files/${file.id}?uploadType=media`; await _localFs.localFileSystem.uploadFile(media.localPath, remoteUri, { method: 'PATCH', uploadType: 'binary', headers: { 'Authorization': `Bearer ${this.options.accessToken}`, 'Content-Type': media.mimeType } }); } } async createDirectory(metadata) { await this.request(`/files`, { method: 'POST', headers: { 'Content-Type': `application/json` }, body: JSON.stringify({ ...metadata, mimeType: _types.MimeTypes.FOLDER }) }); } async updateFile(fileId, media) { if ('body' in media) { await this.request(`/files/${fileId}`, { queryParameters: { uploadType: 'media' }, method: 'PATCH', headers: { 'Content-Type': media.mimeType, 'Content-Length': media.body.length.toString() }, body: media.body, baseUrl: BASE_UPLOAD_URL }); } else { const remoteUri = `${BASE_UPLOAD_URL}/files/${fileId}?uploadType=media`; await _localFs.localFileSystem.uploadFile(media.localPath, remoteUri, { method: 'PATCH', uploadType: 'binary', headers: { 'Authorization': `Bearer ${this.options.accessToken}`, 'Content-Type': media.mimeType } }); } } async downloadFile(fileId, localPath) { const remoteUri = `${BASE_URL}/files/${fileId}?alt=media`; await _localFs.localFileSystem.downloadFile(remoteUri, localPath, { headers: { Authorization: `Bearer ${this.options.accessToken}` } }); } } exports.default = GoogleDriveApiClient; //# sourceMappingURL=client.js.map