Repository: usergrid-nodejs Updated Branches: refs/heads/master 4f6ba21d7 -> b5c8b4ca6
Implemented and working assets (todo: asset convenience methods) Project: http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/commit/c2e4f0ae Tree: http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/tree/c2e4f0ae Diff: http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/diff/c2e4f0ae Branch: refs/heads/master Commit: c2e4f0ae39a6b1202b749a5fa647b3445cbdc266 Parents: 1458f58 Author: brandon <[email protected]> Authored: Fri Feb 5 17:56:53 2016 -0800 Committer: brandon <[email protected]> Committed: Fri Feb 5 17:56:53 2016 -0800 ---------------------------------------------------------------------- helpers/build.js | 106 ++++++++++++++++++++++---------- lib/asset.js | 75 +++++++++++++++++++++++ lib/entity.js | 8 ++- lib/request.js | 17 +++--- lib/response.js | 1 - package.json | 6 +- tests/lib/asset.test.js | 122 +++++++++++++++++++++++++++++++++++++ tests/lib/client.rest.test.js | 21 ++++++- tests/lib/image.jpg | Bin 0 -> 109055 bytes tests/lib/image_test.jpg | Bin 0 -> 109055 bytes tests/main.test.js | 15 ++++- usergrid.js | 3 +- 12 files changed, 325 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/helpers/build.js ---------------------------------------------------------------------- diff --git a/helpers/build.js b/helpers/build.js index 67fa0c9..0991638 100644 --- a/helpers/build.js +++ b/helpers/build.js @@ -6,6 +6,7 @@ var urljoin = require('url-join'), UsergridQuery = require('../lib/query'), UsergridEntity = require('../lib/entity'), UsergridAuth = require('../lib/auth'), + UsergridAsset = require('../lib/asset'), util = require('util'), version = require('../package.json').version, ok = require('objectkit'), @@ -13,12 +14,44 @@ var urljoin = require('url-join'), var assignPrefabOptions = function(args) { // if a preformatted options argument passed, assign it to options - if (_.isObject(args[0]) && !_.isFunction(args[0]) && args.length <= 2) { + if (_.isObject(args[0]) && !_.isFunction(args[0]) && ok(this).has("method")) { _.assign(this, args[0]) } return this } +var setEntity = function(args) { + this.entity = _.first([this.entity, args[0]].filter(function(property) { + return (property instanceof UsergridEntity) + })) + if (this.entity !== undefined) { + this.type = this.entity.type + } + return this +} + +var setAsset = function(args) { + this.asset = _.first([this.asset, ok(this).getIfExists('entity.asset'), args[1], args[0]].filter(function(property) { + return (property instanceof UsergridAsset) + })) + return this +} + +var setUuidOrName = function(args) { + this.uuidOrName = _.first([ + this.uuidOrName, + this.uuid, + this.name, + ok(this).getIfExists('entity.uuid'), + ok(this).getIfExists('body.uuid'), + ok(this).getIfExists('entity.name'), + ok(this).getIfExists('body.name'), + ok(args).getIfExists('2'), + ok(args).getIfExists('1') + ].filter(_.isString)) + return this +} + var setPathOrType = function(args) { var pathOrType = _.first([ this.type, @@ -48,34 +81,14 @@ var setQuery = function(args) { var setBody = function(args) { this.body = _.first([this.entity, this.body, args[2], args[1], args[0]].filter(function(property) { - return _.isObject(property) && !_.isFunction(property) && !(property instanceof UsergridQuery) + return _.isObject(property) && !_.isFunction(property) && !(property instanceof UsergridQuery) && !(property instanceof UsergridAsset) })) - if (this.body === undefined) { + if (this.body === undefined && this.asset === undefined) { throw new Error(util.format('"body" is required when making a %s request', this.method)) } return this } -var setUuidOrName = function(args) { - this.uuidOrName = _.first([ - this.uuidOrName, - this.uuid, - this.name, - ok(this).getIfExists('entity.uuid'), - ok(this).getIfExists('body.uuid'), - _.isArray(args) ? args[2] : undefined, - _.isArray(args) ? args[1] : undefined - ].filter(_.isString)) - return this -} - -var setEntity = function(args) { - this.entity = _.first([this.entity, args[0]].filter(function(property) { - return (property instanceof UsergridEntity) - })) - return this -} - module.exports = { uri: function(client, options) { return urljoin( @@ -83,7 +96,14 @@ module.exports = { client.orgId, client.appId, options.path || options.type, - _.first([options.uuidOrName, options.uuid, options.name, ""].filter(_.isString)) + options.method !== "POST" ? _.first([ + options.uuidOrName, + options.uuid, + options.name, + ok(options).getIfExists('entity.uuid'), + ok(options).getIfExists('entity.name'), + "" + ].filter(_.isString)) : "" ) }, headers: function(client) { @@ -158,11 +178,11 @@ module.exports = { callback: helpers.cb(args) } assignPrefabOptions.call(options, args) + setEntity.call(options, args) setUuidOrName.call(options, args) setPathOrType.call(options, args) setQs.call(options, args) setQuery.call(options, args) - setEntity.call(options, args) return options }, PUT: function(client, args) { @@ -191,12 +211,12 @@ module.exports = { callback: helpers.cb(args) } assignPrefabOptions.call(options, args) + setEntity.call(options, args) + setAsset.call(options, args) setBody.call(options, args) setUuidOrName.call(options, args) setPathOrType.call(options, args) setQuery.call(options, args) - setEntity.call(options, args) - return options }, POST: function(client, args) { @@ -220,6 +240,8 @@ module.exports = { callback: helpers.cb(args) } assignPrefabOptions.call(options, args) + setEntity.call(options, args) + setAsset.call(options, args) setBody.call(options, args) setPathOrType.call(options, args) return options @@ -246,14 +268,11 @@ module.exports = { callback: helpers.cb(args) } assignPrefabOptions.call(options, args) + setEntity.call(options, args) setUuidOrName.call(options, args) setPathOrType.call(options, args) setQs.call(options, args) - setEntity.call(options, args) setQuery.call(options, args) - if (!_.isString(options.uuidOrName) && options.query === undefined && options.path === undefined) { - throw new Error('"uuidOrName", "query", or "path" is required when making a DELETE request') - } return options }, connection: function(client, method, args) { @@ -414,5 +433,30 @@ module.exports = { ) return options + }, + qs: function(options) { + return (options.query instanceof UsergridQuery) ? { + ql: options.query._ql || undefined, + limit: options.query._limit, + cursor: options.query._cursor + } : options.qs + }, + formData: function(options) { + if (ok(options).getIfExists('asset.data')) { + var formData = {} + formData.file = { + value: options.asset.data, + options: { + filename: ok(options).getIfExists('asset.filename') || UsergridAsset.DEFAULT_FILE_NAME, + contentType: ok(options).getIfExists('asset.contentType') || 'application/octet-stream' + } + } + if (ok(options).has('asset.name')) { + formData.name = options.asset.name + } + return formData + } else { + return undefined + } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/lib/asset.js ---------------------------------------------------------------------- diff --git a/lib/asset.js b/lib/asset.js index e69de29..d5f25b2 100644 --- a/lib/asset.js +++ b/lib/asset.js @@ -0,0 +1,75 @@ +'use strict' + +var Usergrid = require('../usergrid'), + validator = require('validator'), + readChunk = require('read-chunk'), + fileType = require('file-type'), + helpers = require('../helpers'), + stream = require('stream'), + util = require('util'), + ok = require('objectkit'), + _ = require('lodash') + +var UsergridAsset = function() { + var self = this + var args = helpers.args(arguments) + + var __contentType + var __binaryData = [] + + if (args.length === 0) { + throw new Error('A UsergridAsset object was initialized using an empty argument') + } + + if (_.isObject(args[0])) { + _.assign(self, args[0]) + } else { + self.filename = _.isString(args[0]) ? args[0] : undefined + self.data = validator.isBase64(args[1]) || Buffer.isBuffer(args[1]) ? args[1] : [] + self.originalLocation = _.first([args[2], args[1]].filter(function(property) { + return (_.isString(property) && !validator.isBase64(property)) + })) + self.contentType = _.isString(args[3]) ? args[3] : undefined + + stream.PassThrough.call(self) + self._write = function(chunk, encoding, done) { + __binaryData.push(chunk) + done() + } + self.on('finish', function() { + self.data = Buffer.concat(__binaryData) + }) + } + + Object.defineProperty(self, 'contentLength', { + get: function() { + return (self.data) ? self.data.byteLength : 0 + } + }) + + Object.defineProperty(self, 'contentType', { + get: function() { + if (__contentType) { + return __contentType + } else if (Buffer.isBuffer(self.data)) { + __contentType = fileType(self.data).mime + return __contentType + } + }, + set: function(contentType) { + if (contentType) { + __contentType = contentType + } else if (Buffer.isBuffer(self.data)) { + __contentType = fileType(self.data).mime + } + } + }) + + return self +} + +util.inherits(UsergridAsset, stream.PassThrough) + + +module.exports = UsergridAsset +module.exports.DEFAULT_FILE_NAME = 'file' \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/lib/entity.js ---------------------------------------------------------------------- diff --git a/lib/entity.js b/lib/entity.js index cc81e17..cc17872 100644 --- a/lib/entity.js +++ b/lib/entity.js @@ -9,7 +9,7 @@ var UsergridEntity = function() { var self = this var args = helpers.args(arguments) - if (!args[0]) { + if (args.length === 0) { throw new Error('A UsergridEntity object was initialized using an empty argument') } @@ -42,6 +42,8 @@ var UsergridEntity = function() { } }) + self.asset + helpers.setReadOnly(self, ['uuid', 'name', 'type', 'created']) return self @@ -121,7 +123,9 @@ UsergridEntity.prototype = { callback(error, usergridResponse, this) }.bind(this)) }, - attachAsset: function() {}, + attachAsset: function(asset) { + this.asset = asset + }, uploadAsset: function() {}, downloadAsset: function() {}, connect: function() { http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/lib/request.js ---------------------------------------------------------------------- diff --git a/lib/request.js b/lib/request.js index 65dad4a..28e549e 100644 --- a/lib/request.js +++ b/lib/request.js @@ -4,33 +4,34 @@ var request = require('request'), helpers = require('../helpers'), UsergridResponse = require('../lib/response'), UsergridQuery = require('../lib/query'), - util = require('util'), + UsergridAsset = require('../lib/asset'), ok = require('objectkit'), _ = require('lodash') var UsergridRequest = function(options) { + var self = this var client = helpers.client.validate(options.client) var callback = helpers.cb(helpers.args(arguments)) if (!_.isString(options.type) && !_.isString(options.path) && !_.isString(options.uri)) { throw new Error('one of "type" (collection name), "path", or "uri" parameters are required when initializing a UsergridRequest') } + if (!_.includes(['GET', 'PUT', 'POST', 'DELETE'], options.method)) { throw new Error('"method" parameter is required when initializing a UsergridRequest') } var uri = options.uri || helpers.build.uri(client, options) + var formData = helpers.build.formData(options) + var body = ((formData !== undefined) ? undefined : options.body) - request(uri, { + var req = request(uri, { headers: helpers.build.headers(client), - body: options.body, + body: body, json: true, method: options.method, - qs: (options.query instanceof UsergridQuery) ? { - ql: options.query._ql || undefined, - limit: options.query._limit, - cursor: options.query._cursor - } : options.qs + qs: helpers.build.qs(options), + formData: formData }, function(error, response) { var usergridResponse = new UsergridResponse(response) var returnBody = _.first([usergridResponse.user, usergridResponse.users, usergridResponse.entity, usergridResponse.entities, usergridResponse.body].filter(_.isObject)) http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/lib/response.js ---------------------------------------------------------------------- diff --git a/lib/response.js b/lib/response.js index 63a4b20..3cd21e1 100644 --- a/lib/response.js +++ b/lib/response.js @@ -26,7 +26,6 @@ var UsergridResponse = function(response) { } return entity }) - _.assign(self, { metadata: _.cloneDeep(response.body), entities: entities http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/package.json ---------------------------------------------------------------------- diff --git a/package.json b/package.json index bfd5a32..d949ff1 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,16 @@ "author": "Brandon Shelley", "dependencies": { "async": "latest", + "file-type": "^3.4.0", "lodash": "~4.0", "lodash-inflection": "latest", "lodash-uuid": "latest", "objectkit": "latest", + "read-chunk": "^1.0.1", "request": "latest", - "url-join": "latest" + "through2": "^2.0.0", + "url-join": "latest", + "validator": "^4.5.0" }, "description": "The official Node.js SDK for Usergrid", "devDependencies": { http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/tests/lib/asset.test.js ---------------------------------------------------------------------- diff --git a/tests/lib/asset.test.js b/tests/lib/asset.test.js new file mode 100644 index 0000000..0911ea8 --- /dev/null +++ b/tests/lib/asset.test.js @@ -0,0 +1,122 @@ +'use strict' + +var should = require('should'), + urljoin = require('url-join'), + config = require('../../helpers').config, + UsergridEntity = require('../../lib/entity'), + UsergridAsset = require('../../lib/asset'), + UsergridClient = require('../../lib/client'), + util = require('util'), + request = require('request'), + fs = require('fs'), + _ = require('lodash') + +var _slow = 6000, + _timeout = 12000, + filename = 'old_man', + file = __dirname + '/image.jpg', + testFile = __dirname + '/image_test.jpg', + expectedContentLength = 109055 + +describe('init from fs.readFile()', function() { + var asset = new UsergridAsset(filename, file) + + before(function(done) { + fs.readFile(file, function(err, data) { + asset.data = data + done() + }) + }) + + it('asset.data should be a binary Buffer', function() { + asset.data.should.be.a.buffer + }) + + it('asset.contentType should be inferred from Buffer', function() { + asset.contentType.should.equal('image/jpeg') + }) + + it(util.format('asset.contentLength should be %s bytes', expectedContentLength), function() { + asset.contentLength.should.equal(expectedContentLength) + }) +}) + +describe('init from piped writable stream', function() { + var asset = new UsergridAsset(filename, file) + var writeTestAsset = new UsergridAsset('image_test', testFile) + before(function(done) { + var stream = fs.createReadStream(file).pipe(asset), + writeTest + stream.on('finish', function() { + fs.writeFile(testFile, asset.data) + writeTest = fs.createReadStream(file).pipe(writeTestAsset) + writeTest.on('finish', function() { + done() + }) + }) + }) + + it('asset.data should be a binary Buffer', function() { + asset.data.should.be.a.buffer + }) + + it('asset.contentType should be inferred from Buffer', function() { + asset.contentType.should.equal('image/jpeg') + }) + + it(util.format('asset.contentLength should be %s bytes', expectedContentLength), function() { + asset.contentLength.should.equal(expectedContentLength) + }) + + it('should write an identical asset to the filesystem', function() { + writeTestAsset.contentType.should.equal('image/jpeg') + writeTestAsset.contentLength.should.equal(expectedContentLength) + }) +}) + +describe('upload via client.POST to a specific entity', function() { + + this.slow(_slow) + this.timeout(_timeout) + + var client = new UsergridClient() + it('should upload a binary asset and create a new entity', function(done) { + var asset = new UsergridAsset(filename, file) + fs.createReadStream(file).pipe(asset).on('finish', function() { + client.POST(config.test.collection, asset, function(err, assetResponse, entityWithAsset) { + assetResponse.statusCode.should.equal(200) + entityWithAsset.should.have.property('file-metadata') + entityWithAsset['file-metadata'].should.have.property('content-type').equal('image/jpeg') + entityWithAsset['file-metadata'].should.have.property('content-length').equal(expectedContentLength) + entityWithAsset.remove(client) + done() + }) + }) + }) +}) + +describe('upload via client.PUT to a specific entity', function() { + + this.slow(_slow) + this.timeout(_timeout) + + var client = new UsergridClient() + it('should upload a binary asset to an existing entity', function(done) { + var entity = new UsergridEntity({ + type: config.test.collection, + name: "AssetTestPUT", + }) + var asset = new UsergridAsset(filename, file) + client.PUT(entity, function(err, entityResponse, createdEntity) { + fs.createReadStream(file).pipe(asset).on('finish', function() { + client.PUT(createdEntity, asset, function(err, assetResponse, entityWithAsset) { + assetResponse.statusCode.should.equal(200) + entityWithAsset.should.have.property('file-metadata') + entityWithAsset['file-metadata'].should.have.property('content-type').equal('image/jpeg') + entityWithAsset['file-metadata'].should.have.property('content-length').equal(expectedContentLength) + done() + }) + }) + }) + }) +}) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/tests/lib/client.rest.test.js ---------------------------------------------------------------------- diff --git a/tests/lib/client.rest.test.js b/tests/lib/client.rest.test.js index 75f17d2..7cf7074 100644 --- a/tests/lib/client.rest.test.js +++ b/tests/lib/client.rest.test.js @@ -3,6 +3,7 @@ var should = require('should'), urljoin = require('url-join'), config = require('../../helpers').config, + chance = new require('chance').Chance(), UsergridClient = require('../../lib/client'), UsergridEntity = require('../../lib/entity'), UsergridQuery = require('../../lib/query'), @@ -135,6 +136,22 @@ describe('POST()', function() { }) }) + it('should support creating an entity by passing a UsergridEntity object with a unique name', function(done) { + + this.slow(_slow) + this.timeout(_timeout) + + var entity = new UsergridEntity({ + type: config.test.collection, + name: chance.word() + }) + client.POST(entity, function(err, usergridResponse) { + usergridResponse.entity.should.be.an.Object().with.property('name').equal(entity.name) + usergridResponse.entity.remove(client) + done() + }) + }) + it('should support creating an entity by passing type and a body object', function(done) { this.slow(_slow) @@ -203,9 +220,7 @@ describe('POST()', function() { } client.POST(options, function(err, usergridResponse) { - usergridResponse.entities.forEach(function(entity) { - entity.should.be.an.Object().with.property('restaurant').equal(entity.restaurant) - }) + usergridResponse.entity.should.be.an.Object().with.property('restaurant').equal(usergridResponse.entity.restaurant) done() }) }) http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/tests/lib/image.jpg ---------------------------------------------------------------------- diff --git a/tests/lib/image.jpg b/tests/lib/image.jpg new file mode 100644 index 0000000..32f4fa3 Binary files /dev/null and b/tests/lib/image.jpg differ http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/tests/lib/image_test.jpg ---------------------------------------------------------------------- diff --git a/tests/lib/image_test.jpg b/tests/lib/image_test.jpg new file mode 100644 index 0000000..32f4fa3 Binary files /dev/null and b/tests/lib/image_test.jpg differ http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/tests/main.test.js ---------------------------------------------------------------------- diff --git a/tests/main.test.js b/tests/main.test.js index 87a2d05..581411e 100644 --- a/tests/main.test.js +++ b/tests/main.test.js @@ -2,6 +2,7 @@ // module config var should = require('should'), + validator = require('validator'), _ = require('lodash') _.mixin(require('lodash-uuid')) @@ -9,9 +10,17 @@ _.mixin(require('lodash-uuid')) should.Assertion.add('uuid', function() { this.params = { operator: 'to be a valid uuid' - }; + } this.assert(_.isUuid(this.obj)) }) + +should.Assertion.add('buffer', function() { + this.params = { + operator: 'to be buffer data' + } + this.assert(Buffer.isBuffer(this.obj)) +}) + // end module config describe('Usergrid initialization', function() { @@ -60,4 +69,8 @@ describe('UsergridEntity', function() { describe('UsergridUser', function() { return require('./lib/user.test') +}) + +describe('UsergridAsset', function() { + return require('./lib/asset.test') }) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/usergrid-nodejs/blob/c2e4f0ae/usergrid.js ---------------------------------------------------------------------- diff --git a/usergrid.js b/usergrid.js index 330fffa..d4fe3cd 100644 --- a/usergrid.js +++ b/usergrid.js @@ -1,7 +1,5 @@ 'use strict' -var UsergridClient = require('./lib/client') - var Usergrid = { isInitialized: false, isSharedInstance: true, @@ -10,6 +8,7 @@ var Usergrid = { if (self.isInitialized) { return self } + var UsergridClient = require('./lib/client') Object.setPrototypeOf(Usergrid, new UsergridClient(options)) UsergridClient.call(self) self.isInitialized = true
