Skip to content

Commit

Permalink
upgrade - rewrite - test with googleapis@30.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
DrPaulBrewer committed May 13, 2018
1 parent 171922e commit c58b818
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 6,
"ecmaVersion": 2017,
"sourceType": "module"
},
"rules": {
Expand Down
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@

Initialize googleapi's Google Drive[tm] nodejs client, decorated with some useful 3rd party extensions.

## new in v4

* (hopefully) now compatible with googleapis@30.0.0
* Initialization has changed slightly, because googleapis@30.0.0 uses named exports
* Now promise/async compatible at both clasic `drive` and extensions `drive.x`
* mostly the same API as v3, minimal changes. Still uses `request` for resumable upload. Will move to axios for `v5`.
* The `drive` functionality is vanilla GoogleApis and from their changes you may need to `.then((resp)=>(resp.data))`
* The `drive.x` functionality is mostly the same, except promise-yielding functions are now explicitly marked as async function


## Usage

### Install

Pre-requisites are `googleapis@24.0.0` and `request`
Pre-requisites are `googleapis@30.0.0` and `request`

**v3 limitation** `googleapis@25.0.0` introduces incompatibilities with `decorated-google-drive@3`
**Note:** To use older googleapi versions, such as `googleapis@24.0.0` try `decorated-google-drive@3`

npm i googleapis@24.0.0 -S
npm i googleapis@30.0.0 -S
npm i request -S
npm i decorated-google-drive -S

Expand All @@ -21,7 +31,7 @@ Pass the googleapis and request modules, and your keys and tokens. The `keys` ar
The `tokens` are obtained when a user "Logs in with Google" in your app. There is various middleware for "Log in with Google", such as
`passport` for `express`, `grant` and `bell` for `hapi`, and even a client-Javascript side library you can get from Google.

const googleapis = require('googleapis'); // worked with googleapis-22.20
const {google} = require('googleapis'); // works with googleapis-30.0.0
const request = require('request'); // worked with request-2.83.0
const driveX = require('decorated-google-drive');
const keys = {
Expand All @@ -35,7 +45,7 @@ The `tokens` are obtained when a user "Logs in with Google" in your app. There
access_token: "the-latest-access-token-your-app-received-the-most-recent-time-the-visitor-logged-in,
expiry_time: Date.now()+1000*60*59 // 59 minutes
};
const drive = driveX(googleapis, request, keys, tokens);
const drive = driveX(google, request, keys, tokens);

Now:
* `drive` contains a googleapis.drive official client
Expand All @@ -45,7 +55,7 @@ Now:
All extensions are written in terms of calls to `googleapis.drive`, it is simply that some of the techniques are tedious or less than obvious,
and so it is useful to repackage these as extensions.

The original drive client uses callbacks. The `drive.x` extensions return Promises.
Both the original drive client in `drive` and the `drive.x` extensions are async functions and return Promises.

### Decorate an existing vanilla googleapis.drive instance

Expand Down
99 changes: 48 additions & 51 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
/* eslint-env: node */
/* eslint max-params: "off" */

const pify = require('pify');
const pReduce = require('p-reduce');
const Boom = require('boom');
const digestStream = require('digest-stream');
Expand Down Expand Up @@ -37,10 +36,12 @@ function extensions(drive, request, rootFolderId, spaces) {
});
return allfields;
}

function getdata(resp){ return resp.data; }

function driveAboutMe(_fields) {
async function driveAboutMe(_fields) {
const fields = _fields || "user,storageQuota";
return pify(drive.about.get)({ fields });
return drive.about.get({ fields }).then(getdata);
}

x.aboutMe = driveAboutMe;
Expand All @@ -63,7 +64,7 @@ function extensions(drive, request, rootFolderId, spaces) {
if (!searchTerms.trashed)
searchTerms.trashed = false;

return function (parent, name) {
return async function (parent, name) {
const search = Object.assign({}, searchTerms, { parent, name });
const searchString = ssgd(search, allowMatchAllFiles);
const params = {
Expand All @@ -77,16 +78,13 @@ function extensions(drive, request, rootFolderId, spaces) {

// see https://developers.google.com/drive/v3/web/search-parameters

return new Promise(function (resolve, reject) {
drive.files.list(params, function (err, resp) {
if (err) return reject(err);
// add isFolder boolean property to files, comparing mimeType to the Google Drive folder mimeType
if (Array.isArray(resp.files))
resp.files.forEach(addIsFolder);
const result = { parent, name, searchTerms, limit, unique, isSearchResult: true, files: resp.files };
return resolve(result);
});
});
const axiosresp = await drive.files.list(params);
const resp = getdata(axiosresp);
// add isFolder boolean property to files, comparing mimeType to the Google Drive folder mimeType
if (Array.isArray(resp.files))
resp.files.forEach(addIsFolder);
const result = { parent, name, searchTerms, limit, unique, isSearchResult: true, files: resp.files };
return result;
};
}

Expand All @@ -108,10 +106,10 @@ function extensions(drive, request, rootFolderId, spaces) {
x.checkSearch = checkSearch;

function driveJanitor(fileListProperty, successProperty) {
function deleteFile(file) {
return pify(drive.files.delete)({ fileId: file.id });
async function deleteFile(file) {
return drive.files.delete({ fileId: file.id }).then(getdata);
}
return function (info) {
return async function (info) {
if (successProperty) info[successProperty] = false;
let files = (fileListProperty) ? info[fileListProperty] : info;
if (files && files.id) files = [files];
Expand All @@ -123,30 +121,30 @@ function extensions(drive, request, rootFolderId, spaces) {
return info;
})
);
return Promise.resolve(info);
return info;
};
}

x.janitor = driveJanitor;

function getFolderId(folderIdOrObject) {
async function getFolderId(folderIdOrObject) {
if (typeof(folderIdOrObject) === 'object') {
if (folderIdOrObject.id) {
if (folderIdOrObject.mimeType === folderMimeType)
return Promise.resolve(folderIdOrObject.id);
return folderIdOrObject.id;
}
}
if (typeof(folderIdOrObject) === 'string') {
return Promise.resolve(folderIdOrObject);
return folderIdOrObject;
}
return Promise.reject(Boom.badRequest(null, { folder: folderIdOrObject }));
throw Boom.badRequest(null, { folder: folderIdOrObject });
}

x.getFolderId = getFolderId;

function driveStepRight() {
const search = driveSearcher({ unique: true });
return function (folderIdOrObject, name) {
return async function (folderIdOrObject, name) {
return (getFolderId(folderIdOrObject)
.then((parentId) => (search(parentId, name)))
.then(checkSearch)
Expand All @@ -160,7 +158,7 @@ function extensions(drive, request, rootFolderId, spaces) {
// see https://developers.google.com/drive/v3/web/folder

function driveFolderCreator() {
return function (f, name) {
return async function (f, name) {
return (getFolderId(f)
.then((parentFolderId) => {
const mimeType = folderMimeType;
Expand All @@ -169,10 +167,10 @@ function extensions(drive, request, rootFolderId, spaces) {
name,
parents: [parentFolderId]
};
return pify(drive.files.create)({
return drive.files.create({
resource: metadata,
fields: 'id, mimeType, name, parents'
}).then(addNew).then(addIsFolder);
}).then(getdata).then(addNew).then(addIsFolder);
})
);
};
Expand All @@ -183,7 +181,7 @@ function extensions(drive, request, rootFolderId, spaces) {
function driveFolderFactory() {
const stepper = driveStepRight();
const creator = driveFolderCreator();
return function (f, name) {
return async function (f, name) {
return (stepper(f, name)
.catch((e) => {
if ((e.isBoom) && (e.typeof === Boom.notFound)) return creator(f, name);
Expand All @@ -195,46 +193,46 @@ function extensions(drive, request, rootFolderId, spaces) {

x.folderFactory = driveFolderFactory;

function driveFindPath(path) {
async function driveFindPath(path) {
const parts = path.split('/').filter((s) => (s.length > 0));
const stepper = driveStepRight();
return pReduce(parts, stepper, rootFolderId);
}

x.findPath = driveFindPath;

function driveContents(fileId, mimeType) {
const getFile = pify(drive.files.get)({ fileId, spaces, alt: 'media' });
async function driveContents(fileId, mimeType) {
const getFile = drive.files.get({ fileId, spaces, alt: 'media' }).then(getdata);
if (!mimeType)
return getFile;
return (getFile
.catch((e) => {
if (e.toString().includes("Use Export"))
return (pify(drive.files.export)({ fileId, spaces, mimeType }));
return (drive.files.export({ fileId, spaces, mimeType }).then(getdata));
throw e;
})
);
}

x.contents = driveContents;

function driveDownload(path, mimeType) {
async function driveDownload(path, mimeType) {
return driveFindPath(path).then((file) => (driveContents(file.id, mimeType)));
}

x.download = driveDownload;

function driveCreatePath(path) {
async function driveCreatePath(path) {
const parts = path.split('/').filter((s) => (s.length > 0));
const dff = driveFolderFactory();
return pReduce(parts, dff, rootFolderId);
}

x.createPath = driveCreatePath;

function driveUpdateMetadata(fileId, metadata) {
async function driveUpdateMetadata(fileId, metadata) {
const fields = addFieldsFromKeys('id,name,mimeType,modifiedTime,size,parents', metadata);
return pify(drive.files.update)({ fileId, fields, resource: metadata });
return drive.files.update({ fileId, fields, resource: metadata }).then(getdata);
}

x.updateMetadata = driveUpdateMetadata;
Expand All @@ -256,35 +254,34 @@ function extensions(drive, request, rootFolderId, spaces) {
x.nameFrom = nameFrom;

// for url override see end of http://google.github.io/google-api-nodejs-client/22.2.0/index.html

// legacy url: "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable"

function driveUploadDirector(parentFolderOrId) {
return function (metadata) {
const resumableUploadURL = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable";
return async function (metadata) {
return (
getFolderId(parentFolderOrId)
.then((parent) => (
new Promise(function (resolve, reject) {
.then((parent) => {
const meta = Object.assign({}, metadata, { parents: [parent], spaces });
const req = drive.files.create({
return drive.files.create({
uploadType: 'resumable',
resource: meta,
fields: 'id,name,mimeType,md5Checksum,parents'
}, {
url: "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable"
});
req.on('response', (response) => {
const url = response.headers.location;
resolve(url);
});
req.on('error', (e) => (reject(e)));
})
))
},
{
url: resumableUploadURL
}).then((response)=>(response.headers.location));
}
)
);
};
}

x.uploadDirector = driveUploadDirector;

function streamToUrl(localStream, mimeType) {
return function (url) {
return async function (url) {
if ((typeof(url) === "string") && (url.startsWith("https://"))) {
const driveupload = {
method: 'POST',
Expand Down Expand Up @@ -331,7 +328,7 @@ function extensions(drive, request, rootFolderId, spaces) {

x.streamToUrl = streamToUrl;

function upload2({ folderPath, folderId, name, stream, mimeType, createPath, clobber }) {
async function upload2({ folderPath, folderId, name, stream, mimeType, createPath, clobber }) {
function requireString(v, l, k) {
if ((typeof(v) !== 'string') || (v.length < l))
throw new Error("drive.x.upload2, invalid parameter " + k + ", requires string of length at least " + l + " chars");
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
"devDependencies": {
"assert": "^1.4.1",
"eslint": "^4.19.1",
"googleapis": "^24.0.0 <25",
"mocha": "^4.1.0",
"googleapis": "^30.0.0",
"mocha": "^5.1.1",
"request": "^2.83.0",
"should": "^13.2.1",
"string-to-stream": "^1.1.0"
Expand All @@ -38,7 +38,6 @@
"boom": "^7.1.1",
"digest-stream": "^2.0.0",
"p-reduce": "^1.0.0",
"pify": "^3.0.0",
"search-string-for-google-drive": "^1.0.0"
}
}
11 changes: 5 additions & 6 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@

const assert = require('assert');
require('should');
const google = require('googleapis');
const {google} = require('googleapis');
const request = require('request');
const fs = require('fs');
const str = require('string-to-stream');
const Boom = require('boom');
const pify = require('pify');
const folderMimeType = 'application/vnd.google-apps.folder';

const keys = {
Expand Down Expand Up @@ -62,7 +61,7 @@ describe('decorated-google-drive:', function () {
it('drive.about.get still works, as well, and the outputs match', function () {
return Promise.all([
drive.x.aboutMe(),
pify(drive.about.get)({ fields: 'user, storageQuota' })
drive.about.get({ fields: 'user, storageQuota' }).then((res)=>(res.data))
]).then(([A, B]) => {
A.should.deepEqual(B);
});
Expand Down Expand Up @@ -125,9 +124,9 @@ describe('decorated-google-drive:', function () {
uploadResult.parents[0].should.be.type('string');
});
it("the parents[0] folder should have the name 'Files'", function (done) {
drive.files.get({ fileId: uploadResult.parents[0] }, function (e, data) {
drive.files.get({ fileId: uploadResult.parents[0] }, function (e, response) {
if (e) throw e;
data.name.should.equal('Files');
response.data.name.should.equal('Files');
done();
});
});
Expand Down Expand Up @@ -238,7 +237,7 @@ describe('decorated-google-drive:', function () {
// checks response from drive.x.updateMetadata
info.appProperties.role.should.equal('documentation');
info.description.should.equal('read this first');
return pify(drive.files.get)({ fileId: info.id, fields: "id,name,description,appProperties" });
return drive.files.get({ fileId: info.id, fields: "id,name,description,appProperties" }).then((resp)=>(resp.data));
}).then((info) => {
// checks response from subsequent drive.files.get
info.description.should.equal("read this first");
Expand Down
Loading

0 comments on commit c58b818

Please sign in to comment.