1 /**
  2  * @fileOverview AWS Container.
  3  */
  4 
  5 /**
  6  * @name provider.aws.blob
  7  */
  8 (function () {
  9   var util = require('util'),
 10     utils = require("../../../utils"),
 11     DummyRequest = require("../../../request").DummyRequest,
 12     Blob = require("./blob").Blob,
 13     BaseContainer = require("../../../base/blob").Container,
 14     AwsContainer;
 15 
 16   /**
 17    * Container class.
 18    *
 19    * @param {base.Connection} conn    Connection object.
 20    * @param {Object}          attrs   Attributes.
 21    * @config {string}         name    Name.
 22    * @config {string}         created Creation date.
 23    * @extends base.blob.Container
 24    * @exports AwsContainer as provider.aws.blob.Container
 25    * @constructor
 26    */
 27   AwsContainer = function () {
 28     BaseContainer.apply(this, arguments);
 29   };
 30 
 31   util.inherits(AwsContainer, BaseContainer);
 32 
 33   /**
 34    * Upgrade to array of single element if not already.
 35    */
 36   function mkArray(val) {
 37     return Array.isArray(val) ? val : [val];
 38   }
 39 
 40   /**
 41    * @see base.blob.Container#_createBlob
 42    * @private
 43    */
 44   AwsContainer.prototype._createBlob = function (name) {
 45     return new Blob(this, { name: name });
 46   };
 47 
 48   /**
 49    * GET Container from cloud.
 50    *
 51    * **Note**: AWS cannot tell if the container already exists on a PUT,
 52    * so ``alreadyCreated`` result is always false. Google Storage can.
 53    *
 54    * @see base.blob.Container#get
 55    */
 56   AwsContainer.prototype.get = function (options) {
 57     var self = this,
 58       conn = self.connection,
 59       auth = conn.authentication,
 60       meta = utils.extractMeta(options),
 61       alreadyCreated = false,
 62       getResults;
 63 
 64     // Strict boolean values for options to preserve defaults.
 65     options = utils.extend(options);
 66     options.validate = options.validate === true;
 67     options.create = options.create === true;
 68 
 69     /** @private */
 70     getResults = function () {
 71       return {
 72         container: self,
 73         alreadyCreated: alreadyCreated
 74       };
 75     };
 76 
 77     if (options.create) {
 78       // Do a PUT and trap the "already exists" error.
 79       return auth.createRequest(utils.extend(meta, {
 80         encoding: 'utf8',
 81         method: "PUT",
 82         path: "/",
 83         headers: {
 84           'content-length': 0,
 85           'host': auth.authUrl(self.name)
 86         },
 87         errorFn: function (err, request, response) {
 88           // AlreadyOwnedByYou is OK.
 89           var translated = conn._translateErrors(err, request, response);
 90           if (translated && translated.isAlreadyOwnedByYou()) {
 91             alreadyCreated = true;
 92             request.emit('end', getResults());
 93           } else {
 94             request.emit('error', translated || err);
 95           }
 96         },
 97         resultsFn: getResults
 98       }));
 99     } else if (options.validate) {
100       // Do an empty list on the bucket.
101       alreadyCreated = true;
102       return auth.createRequest(utils.extend(meta, {
103         encoding: 'utf8',
104         path: "/?max-keys=0",
105         headers: {
106           'content-length': 0,
107           'host': auth.authUrl(self.name)
108         },
109         errorFn: function (err, request, response) {
110           var translated = conn._translateErrors(err, request, response);
111           request.emit('error', translated || err);
112         },
113         resultsFn: getResults
114       }));
115     } else {
116       // Return empty object.
117       return new DummyRequest(utils.extend(meta, {
118         resultsFn: getResults
119       }));
120     }
121   };
122 
123   /**
124    * @see base.blob.Container#del
125    */
126   AwsContainer.prototype.del = function (options) {
127     var self = this,
128       conn = self.connection,
129       auth = conn.authentication,
130       meta = utils.extractMeta(options),
131       notFound = false,
132       getResults;
133 
134     options = options || {};
135 
136     /** @private */
137     getResults = function () {
138       return {
139         container: self,
140         notFound: notFound
141       };
142     };
143 
144     return auth.createRequest(utils.extend(meta, {
145       encoding: 'utf8',
146       method: "DELETE",
147       path: "/",
148       headers: utils.extend(options.headers, {
149         'content-length': 0,
150         'host': auth.authUrl(self.name)
151       }),
152       errorFn: function (err, request, response) {
153         var trans = conn._translateErrors(err, request, response);
154         if (trans && (trans.isNotFound() || trans.isInvalidName())) {
155            // NotFound, InvalidName is OK.
156           notFound = true;
157           request.emit('end', getResults());
158         } else {
159           request.emit('error', trans || err);
160         }
161       },
162       resultsFn: getResults
163     }));
164   };
165 
166   /**
167    * @see base.blob.Container#getBlobs
168    */
169   AwsContainer.prototype.getBlobs = function (options) {
170     var self = this,
171       conn = self.connection,
172       auth = conn.authentication,
173       meta = utils.extractMeta(options),
174       params = {};
175 
176     function _paramsAdd(key, value) {
177       if (value) {
178         params[key] = value;
179       }
180     }
181 
182     // Assemble parameters
183     options = options || {};
184     params['max-keys'] = options.maxResults || 1000;
185     _paramsAdd('prefix', options.prefix);
186     _paramsAdd('delimiter', options.delimiter);
187     _paramsAdd('marker', options.marker);
188 
189     return conn.authentication.createXmlRequest(utils.extend(meta, {
190       encoding: 'utf8',
191       path: "/",
192       params: params,
193       headers: utils.extend(options.headers, {
194         'content-length': 0,
195         'host': auth.authUrl(self.name)
196       }),
197       resultsFn: function (results) {
198         var prefixes = mkArray(results.CommonPrefixes || []),
199           keys = mkArray(results.Contents || []),
200           hasNext = results.IsTruncated === 'true',
201           blobs = [],
202           dirNames = [];
203 
204         // Blobs.
205         keys.forEach(function (obj) {
206           blobs.push(new Blob(self, {
207             name: obj.Key,
208             lastModified: obj.LastModified,
209             size: obj.Size,
210             etag: obj.ETag
211           }));
212         });
213 
214         // Pseudo-directories.
215         prefixes.forEach(function (obj) {
216           dirNames.push(obj.Prefix);
217         });
218 
219         return {
220           blobs: blobs,
221           dirNames: dirNames,
222           hasNext: hasNext
223         };
224       }
225     }));
226   };
227 
228   module.exports.Container = AwsContainer;
229 }());
230