Skip to content

Instantly share code, notes, and snippets.

@hussainb
Forked from MrToph/google-drive.js
Created December 24, 2017 10:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hussainb/add3756a0981c5098f0cc4c8966c9b0d to your computer and use it in GitHub Desktop.
Save hussainb/add3756a0981c5098f0cc4c8966c9b0d to your computer and use it in GitHub Desktop.
Google Drive API v3 fetch
import GoogleSignIn from 'react-native-google-sign-in'
const url = 'https://www.googleapis.com/drive/v3'
const uploadUrl = 'https://www.googleapis.com/upload/drive/v3'
const boundaryString = 'foo_bar_baz' // can be anything unique, needed for multipart upload https://developers.google.com/drive/v3/web/multipart-upload
let apiToken = null
export const configureGoogleSignIn = async () => GoogleSignIn.configure({
// https://developers.google.com/identity/protocols/googlescopes
scopes: ['https://www.googleapis.com/auth/drive.appdata'],
shouldFetchBasicProfile: true,
})
export function setApiToken(token) {
apiToken = token
}
function parseAndHandleErrors(response) {
if (response.ok) {
return response.json()
}
return response.json()
.then((error) => {
throw new Error(JSON.stringify(error))
})
}
function configureGetOptions() {
const headers = new Headers()
headers.append('Authorization', `Bearer ${apiToken}`)
return {
method: 'GET',
headers,
}
}
function configurePostOptions(bodyLength, isUpdate = false) {
const headers = new Headers()
headers.append('Authorization', `Bearer ${apiToken}`)
headers.append('Content-Type', `multipart/related; boundary=${boundaryString}`)
headers.append('Content-Length', bodyLength)
return {
method: isUpdate ? 'PATCH' : 'POST',
headers,
}
}
function createMultipartBody(body, isUpdate = false) {
// https://developers.google.com/drive/v3/web/multipart-upload defines the structure
const metaData = {
name: 'data.json',
description: 'Backup data for my app',
mimeType: 'application/json',
}
// if it already exists, specifying parents again throws an error
if (!isUpdate) metaData.parents = ['appDataFolder']
// request body
const multipartBody = `\r\n--${boundaryString}\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n`
+ `${JSON.stringify(metaData)}\r\n`
+ `--${boundaryString}\r\nContent-Type: application/json\r\n\r\n`
+ `${JSON.stringify(body)}\r\n`
+ `--${boundaryString}--`
return multipartBody
}
// uploads a file with its contents and its meta data (name, description, type, location)
export function uploadFile(content, existingFileId) {
const body = createMultipartBody(content, !!existingFileId)
const options = configurePostOptions(body.length, !!existingFileId)
return fetch(`${uploadUrl}/files${existingFileId ? `/${existingFileId}` : ''}?uploadType=multipart`, {
...options,
body,
})
.then(parseAndHandleErrors)
}
// Looks for files with the specified file name in your app Data folder only (appDataFolder is a magic keyword)
function queryParams() {
return encodeURIComponent("name = 'data.json' and 'appDataFolder' in parents")
}
// returns the files meta data only. the id can then be used to download the file
export function getFile() {
const qParams = queryParams()
const options = configureGetOptions()
return fetch(`${url}/files?q=${qParams}&spaces=appDataFolder`, options)
.then(parseAndHandleErrors)
.then((body) => {
if (body && body.files && body.files.length > 0) return body.files[0]
return null
})
}
// download the file contents given the id
export function downloadFile(existingFileId) {
const options = configureGetOptions()
if (!existingFileId) throw new Error('Didn\'t provide a valid file id.')
return fetch(`${url}/files/${existingFileId}?alt=media`, options)
.then(parseAndHandleErrors)
}
//////////////////////////////////
// Downloading in a redux-thunk //
//////////////////////////////////
export const dispatchGoogleDrive = apiToken => (dispatch) => {
dispatch({ type: ActionNames.dataRestoreStart })
setApiToken(apiToken)
return getWorkoutFile()
.then((file) => {
if (file) {
return downloadFile(file.id)
}
throw new Error('No existing backup file found.')
})
.then((data) => {
dispatch({
type: ActionNames.dataRestoreFinished,
payload: data,
})
})
.catch(err => dispatch({ type: ActionNames.dataRestoreError, payload: err }))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment