Repository: minio/minio-js (js)

Issue:
presignedPostPolicy is incorrectly promisified
`presignedPostPolicy` callback has 3 arguments, and `promisify` helper function only handles two, therefore when using  promisified version I cannot obtain formData

Pull request:
Consolidate callback params in presignedPostPolicy
Consolidate callback params to support promise
Fixes #653

Patch:
diff --git a/docs/API.md b/docs/API.md
index 030f4ff..6e34319 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -768,7 +768,7 @@ __Parameters__
 | Param  |  Type | Description  |
 |---|---|---|
 | `policy`  | _object_  | Policy object created by minioClient.newPostPolicy() |
-| `callback(err, postURL, formData)`  | _function_  | Callback function is called with non `null` err value in case of error. `postURL` will be the URL using which the object can be uploaded using POST request. `formData` is the object having key/value pairs for the Form data of POST body. If no callback is passed, a `Promise` is returned. |
+| `callback(err, {postURL, formData})`  | _function_  | Callback function is called with non `null` err value in case of error. `postURL` will be the URL using which the object can be uploaded using POST request. `formData` is the object having key/value pairs for the Form data of POST body. If no callback is passed, a `Promise` is returned. |
 
 
 Create policy:
@@ -809,11 +809,11 @@ POST your content from the browser using `superagent`:
 
 
 ```js
-minioClient.presignedPostPolicy(policy, function(err, postURL, formData) {
+minioClient.presignedPostPolicy(policy, function(err, data) {
   if (err) return console.log(err)
 
-  var req = superagent.post(postURL)
-  _.each(formData, function(value, key) {
+  var req = superagent.post(data.postURL)
+  _.each(data.formData, function(value, key) {
     req.field(key, value)
   })
 
diff --git a/docs/zh_CN/API.md b/docs/zh_CN/API.md
index d8dc3f2..48a9541 100644
--- a/docs/zh_CN/API.md
+++ b/docs/zh_CN/API.md
@@ -764,7 +764,7 @@ __参数__
 | 参数  |  类型 | 描述  |
 |---|---|---|
 | `policy`  | _object_  | 通过minioClient.newPostPolicy()创建的Policy对象。 |
-| `callback(err, postURL, formData)`  | _function_  |如果`err`不是null则代表有错误。`postURL`用于使用post请求上传。`formData`是POST请求体中的键值对对象。如果没有传callback的话，则返回一个`Promise`对象。 |
+| `callback(err, {postURL, formData})`  | _function_  |如果`err`不是null则代表有错误。`postURL`用于使用post请求上传。`formData`是POST请求体中的键值对对象。如果没有传callback的话，则返回一个`Promise`对象。 |
 
 
 创建策略：
@@ -805,11 +805,11 @@ policy.setContentLengthRange(1024, 1024*1024)
 
 
 ```js
-minioClient.presignedPostPolicy(policy, function(err, postURL, formData) {
+minioClient.presignedPostPolicy(policy, function(err, data) {
   if (err) return console.log(err)
 
-  var req = superagent.post(postURL)
-  _.each(formData, function(value, key) {
+  var req = superagent.post(data.postURL)
+  _.each(data.formData, function(value, key) {
     req.field(key, value)
   })
 
diff --git a/examples/presigned-postpolicy.js b/examples/presigned-postpolicy.js
index 8f8b03e..29ee746 100644
--- a/examples/presigned-postpolicy.js
+++ b/examples/presigned-postpolicy.js
@@ -39,13 +39,13 @@ policy.setExpires(expires)
 
 policy.setContentLengthRange(1024, 1024*1024) // Min upload length is 1KB Max upload size is 1MB
 
-s3Client.presignedPostPolicy(policy, function(e, urlStr, formData) {
+s3Client.presignedPostPolicy(policy, function(e, data) {
   if (e) return console.log(e)
   var curl = []
-  curl.push(`curl ${urlStr}`)
-  for (var key in formData) {
-    if (formData.hasOwnProperty(key)) {
-      var value = formData[key]
+  curl.push(`curl ${data.postURL}`)
+  for (var key in data.formData) {
+    if (data.formData.hasOwnProperty(key)) {
+      var value = data.formData[key]
       curl.push(`-F ${key}=${value}`)
     }
   }
diff --git a/package.json b/package.json
index 396c6bc..59a9af8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "minio",
-  "version": "3.2.4",
+  "version": "4.0.0",
   "description": "S3 Compatible Cloud Storage client",
   "main": "./dist/main/minio.js",
   "scripts": {
diff --git a/src/main/minio.js b/src/main/minio.js
index e68a7e9..16cdf25 100644
--- a/src/main/minio.js
+++ b/src/main/minio.js
@@ -1634,7 +1634,7 @@ export class Client {
       var reqOptions = this.getRequestOptions(opts)
       var portStr = (this.port == 80 || this.port === 443) ? '' : `:${this.port.toString()}`
       var urlStr = `${reqOptions.protocol}//${reqOptions.host}${portStr}${reqOptions.path}`
-      cb(null, urlStr, postPolicy.formData)
+      cb(null, {postURL: urlStr,formData: postPolicy.formData})
     })
   }
 
diff --git a/src/main/object-uploader.js b/src/main/object-uploader.js
index 0344b50..0e617b6 100644
--- a/src/main/object-uploader.js
+++ b/src/main/object-uploader.js
@@ -1,4 +1,4 @@
-/*
+﻿/*
  * Minio Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2016 Minio, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@ import * as querystring from 'querystring'
 export default class ObjectUploader extends Transform {
   constructor(client, bucketName, objectName, partSize, contentType, callback) {
     super()
-
+    this.emptyStream = true
     this.client = client
     this.bucketName = bucketName
     this.objectName = objectName
@@ -58,6 +58,7 @@ export default class ObjectUploader extends Transform {
   }
 
   _transform(chunk, encoding, callback) {
+    this.emptyStream = false
     let method = 'PUT'
     let headers = {
       'Content-Length': chunk.length,
@@ -207,6 +208,41 @@ export default class ObjectUploader extends Transform {
   }
 
   _flush(callback) {
+    if (this.emptyStream) {
+      let method = 'PUT'
+      let headers = {
+        'Content-Length': 0,
+        'Content-Type': this.contentType
+      }
+      let options = {
+        method, headers,
+        query: '',
+        bucketName: this.bucketName,
+        objectName: this.objectName
+      }
+
+      this.client.makeRequest(options, '', 200, '', true, (err, response) => {
+        if (err) return callback(err)
+
+        let etag = response.headers.etag
+        if (etag) {
+          etag = etag.replace(/^"/, '').replace(/"$/, '')
+        }
+
+        // Ignore the 'data' event so that the stream closes. (nodejs stream requirement)
+        response.on('data', () => {})
+
+        // Give the etag back, we're done!
+        process.nextTick(() => {
+          this.callback(null, etag)
+        })
+
+        // Because we're sure the stream has ended, allow it to flush and end.
+        callback()
+      })
+
+      return
+    }
     // If it has been uploaded in a single packet, we don't have to do anything.
     if (this.id === null) {
       return
@@ -227,5 +263,4 @@ export default class ObjectUploader extends Transform {
                                           callback()
                                         })
   }
-
 }


Test patch:
diff --git a/src/test/functional/functional-tests.js b/src/test/functional/functional-tests.js
index f7a2e2e..4d73817 100644
--- a/src/test/functional/functional-tests.js
+++ b/src/test/functional/functional-tests.js
@@ -891,10 +891,10 @@ describe('functional tests', function() {
       expires.setSeconds(24 * 60 * 60 * 10)
       policy.setExpires(expires)
 
-      client.presignedPostPolicy(policy, (e, urlStr, formData) => {
+      client.presignedPostPolicy(policy, (e, data) => {
         if (e) return done(e)
-        var req = superagent.post(`${urlStr}`)
-        _.each(formData, (value, key) => req.field(key, value))
+        var req = superagent.post(data.postURL)
+        _.each(data.formData, (value, key) => req.field(key, value))
         req.attach('file', new Buffer([_1byte]), 'test')
         req.end(function(e) {
           if (e) return done(e)