'use strict';
const Factory = require('./Factory');
const File = require('./File');
const trailingSlashIt = require('./trailingSlashIt').trailingSlashIt;
const message = require('../message');
const Permission = require('../util/Permission');
const StatusCode = require('../connector/Message').StatusCode;
const deprecated = require('../util/deprecated');
/**
* @class binding.FileFactory
* @extends binding.Factory<binding.File>
*
* @param {Object=} properties initial properties to set on the file
* @param {...*} arguments Additional constructor params passed through the type constructor
* @return {binding.File} The new managed instance
*/
const FileFactory = Factory.extend(/** @lends binding.FileFactory.prototype */ {
/**
* Creates a new FileFactory for the given type
* @param {EntityManager} db
* @return {binding.FileFactory} A new file factory
* @static
*/
create(db) {
// invoke super method
const factory = Factory.create.call(this, File);
factory.db = db;
return factory;
},
/**
* Creates a new file
* @param {Array<*>=} args Constructor arguments used for instantiation, the constructor will not be called
* when no arguments are passed
* @return {binding.File} A new created file
*/
newInstance(args) {
const instance = Factory.newInstance.call(this, args);
instance.db = this.db;
return instance;
},
/**
* Deserialize the file metadata from a json object back to a new file instance
* @param {json} json The file metadata as json
* @return {binding.File} The deserialize File instance
*/
fromJSON(json) {
const file = this.newInstance([json.id]);
file.fromJSON(json);
return file;
},
/**
* Updates the metadata of the root file directory formally the file "bucket"
* @param {string} bucket The name of the root file directory
* @param {Object<string, util.Permission>} metadata The new metadata for the bucket
* @param {util.Permission=} metadata.load The load permission which grants read access to all stored
* files under the specified bucket
* @param {util.Permission=} metadata.insert The insert permission which is required to insert new
* files into the bucket
* @param {util.Permission=} metadata.update The update permission which is required to update existing
* files within the bucket
* @param {util.Permission=} metadata.delete The delete permission which is required to delete existing
* files within the bucket
* @param {util.Permission=} metadata.query The query permission which is required to list all files
* within a bucket
* @param {binding.FileFactory~bucketMetadataCallback=} doneCallback Invoked if the operation succeeds
* @param {binding.File~failCallback=} failCallback The callback is invoked if any error has occurred
* @return {Promise<void>} A promise which will fulfilled with the updated metadata
*/
saveMetadata(bucket, metadata, doneCallback, failCallback) {
const msg = new message.SetFileBucketMetadata(bucket, metadata);
return this.db.send(msg).then(doneCallback, failCallback);
},
/**
* Gets the metadata of the root folder (formally the file "bucket")
* @param {string} bucket The name of the root file directory
* @param {Object=} options The load metadata options
* @param {Object} [options.refresh=false] Force a revalidation while fetching the metadata
* @param {binding.FileFactory~bucketMetadataCallback=} doneCallback
* The callback is invoked after the metadata is fetched
* @param {binding.File~failCallback=} failCallback The callback is invoked if any error has occurred
* @return {Promise<Object<string, util.Permission>>} A promise which will be fulfilled with the bucket acls
*/
loadMetadata(bucket, options, doneCallback, failCallback) {
const msg = new message.GetFileBucketMetadata(bucket);
// this._db.ensureCacheHeader(this.id, msg, options.refresh);
// do not white list the file, because head-request does not revalidate the cache.
return this.db.send(msg).then((response) => {
const result = {};
Permission.BASE_PERMISSIONS.forEach((key) => {
result[key] = Permission.fromJSON(response.entity[key] || {});
});
return result;
}, (e) => {
if (e.status === StatusCode.OBJECT_NOT_FOUND) {
return null;
}
throw e;
}).then(doneCallback, failCallback);
},
/**
* Lists all the buckets.
* @param {binding.FileFactory~fileListCallback=} doneCallback The callback is invoked with the listed buckets
* @param {binding.File~failCallback=} failCallback The callback is invoked if any error has occurred
* @return {Promise<Array<binding.File>>} The listed buckets.
*/
listBuckets(doneCallback, failCallback) {
return this.db.send(new message.ListBuckets()).then(response => (
response.entity.map(bucket => this.new(bucket + '/'))
)).then(doneCallback, failCallback);
},
/**
* Lists the files (and folders) in the given folder.
*
* @param {binding.File|string} folderOrPath The folder/path to list.
* @param {binding.File} start The file/folder from where to start listing (not included)
* @param {number} count The maximum number of files to return.
* @param {binding.FileFactory~fileListCallback=} doneCallback The callback is invoked with the listed files
* @param {binding.File~failCallback=} failCallback The callback is invoked if any error has occurred
* @return {Promise<Array<binding.File>>} The listed files/folders.
*/
listFiles(folderOrPath, start, count, doneCallback, failCallback) {
let folder;
if (Object(folderOrPath) instanceof String) {
const path = trailingSlashIt(folderOrPath);
folder = this.new({ path });
} else {
folder = folderOrPath;
}
const path = folder.key;
const bucket = folder.bucket;
return this.db.send(new message.ListFiles(bucket, path, start ? start.key : null, count)).then(response => (
response.entity.map(file => this.new(file))
)).then(doneCallback, failCallback);
},
});
/**
* Creates a new file object which represents the file at the given ID
*
* Data provided to the constructor will be uploaded by invoking {@link upload()}.
*
* @param {object|string} fileOptions The fileOptions used to create a new file object, or just the id of the
* file object
* @param {string=} fileOptions.name The filename without the id. If omitted and data is provided as a file object,
* the {@link File#name} will be used otherwise a uuid will be generated.
* @param {string} [fileOptions.parent="/www"] The parent folder which contains the file
* @param {string|Blob|File|ArrayBuffer|json=} fileOptions.data The initial file content, which will be uploaded by
* invoking {@link #upload} later on.
* @param {string=} fileOptions.type A optional type hint used to correctly interpret the provided data
* @param {string=} fileOptions.mimeType The mimType of the file. Defaults to the mimeType of the provided data if
* it is a file object, blob or data-url
* @param {string=} fileOptions.eTag The optional current ETag of the file
* @param {string=} fileOptions.lastModified The optional last modified date
* @param {Acl=} fileOptions.acl The file acl which will be set, if the file is uploaded afterwards
* @param {Object<string,string>} [fileOptions.headers] The custom headers which will be send with the file after
* uploading it
* @return {binding.File} A new file instance
*
* @function
* @name new
* @memberOf binding.FileFactory.prototype
*/
deprecated(FileFactory, '_db', 'db');
/**
* The list files callback is called, with the bucket metadata
* @callback binding.FileFactory~bucketMetadataCallback
* @param {Object<string, util.Permission>} bucketMetadata the bucket metadata
* @return {*} A Promise, result or undefined
*/
/**
* The list files callback is called, with the loaded files
* @callback binding.FileFactory~fileListCallback
* @param {Array<binding.File>} files The listed files
* @return {*} A Promise, result or undefined
*/
module.exports = FileFactory;