Merge remote-tracking branch 'origin/master'

# Conflicts:
#	README.md
diff --git a/README.md b/README.md
index 598f612..e88fab0 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 
 Currently a work in progress; documentation and implementation are subject to change.
 
-> **Note:** Usergrid 2.0 SDK is **not** backwards compatible with 0.1X.XX versions of the SDK as 2.0. If you are currently using 0.1X in a production application, see below for using the 0.1X version with your application.
+_**Note:** This Node.js SDK 2.0 for Usergrid is **not** backwards compatible with 0.1X versions of the SDK. If your application is dependent on the 0.1X set of Node.js APIs, you will need to continue using the 0.1X version (see below for installation instructions)._
 
 ### Current release
 
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 @@
     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 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 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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
         )
 
         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
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
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 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 @@
         }
     })
 
+    self.asset
+
     helpers.setReadOnly(self, ['uuid', 'name', 'type', 'created'])
 
     return self
@@ -121,7 +123,9 @@
             callback(error, usergridResponse, this)
         }.bind(this))
     },
-    attachAsset: function() {},
+    attachAsset: function(asset) {
+        this.asset = asset
+    },
     uploadAsset: function() {},
     downloadAsset: function() {},
     connect: function() {
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 @@
     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))
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 @@
                 }
                 return entity
             })
-
             _.assign(self, {
                 metadata: _.cloneDeep(response.body),
                 entities: entities
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": {
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
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 @@
         })
     })
 
+    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 @@
         }
 
         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()
         })
     })
diff --git a/tests/lib/image.jpg b/tests/lib/image.jpg
new file mode 100644
index 0000000..32f4fa3
--- /dev/null
+++ b/tests/lib/image.jpg
Binary files differ
diff --git a/tests/lib/image_test.jpg b/tests/lib/image_test.jpg
new file mode 100644
index 0000000..32f4fa3
--- /dev/null
+++ b/tests/lib/image_test.jpg
Binary files differ
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 @@
 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('UsergridUser', function() {
     return require('./lib/user.test')
+})
+
+describe('UsergridAsset', function() {
+    return require('./lib/asset.test')
 })
\ No newline at end of file
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 @@
         if (self.isInitialized) {
             return self
         }
+        var UsergridClient = require('./lib/client')
         Object.setPrototypeOf(Usergrid, new UsergridClient(options))
         UsergridClient.call(self)
         self.isInitialized = true