Skip to content

Commit

Permalink
Conform to FilesAdapter Interface (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
dplewis authored Sep 11, 2019
1 parent 03b6a7b commit 83b12bd
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 135 deletions.
1 change: 0 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"no-multiple-empty-lines": 1,
"no-underscore-dangle": 0,
"no-new": 0,
"func-names": 0,
"prefer-destructuring": 0,
"prefer-object-spread": 0
}
Expand Down
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
language: node_js
node_js:
- 10.14.1
branches:
only:
- master
- "/^[0-9]+.[0-9]+.[0-9]+(-.*)?$/"
- "/^greenkeeper/.*$/"
after_success: "./node_modules/.bin/codecov"
deploy:
provider: npm
Expand Down
270 changes: 136 additions & 134 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,157 +11,159 @@ const awsCredentialsDeprecationNotice = function awsCredentialsDeprecationNotice
'See: https://github.com/parse-server-modules/parse-server-s3-adapter#aws-credentials for details');
};

// Creates an S3 session.
// Providing AWS access, secret keys and bucket are mandatory
// Region will use sane defaults if omitted
function S3Adapter(...args) {
const options = optionsFromArguments(args);
this._region = options.region;
this._bucket = options.bucket;
this._bucketPrefix = options.bucketPrefix;
this._directAccess = options.directAccess;
this._baseUrl = options.baseUrl;
this._baseUrlDirect = options.baseUrlDirect;
this._signatureVersion = options.signatureVersion;
this._globalCacheControl = options.globalCacheControl;
this._encryption = options.ServerSideEncryption;
class S3Adapter {
// Creates an S3 session.
// Providing AWS access, secret keys and bucket are mandatory
// Region will use sane defaults if omitted
constructor(...args) {
const options = optionsFromArguments(args);
this._region = options.region;
this._bucket = options.bucket;
this._bucketPrefix = options.bucketPrefix;
this._directAccess = options.directAccess;
this._baseUrl = options.baseUrl;
this._baseUrlDirect = options.baseUrlDirect;
this._signatureVersion = options.signatureVersion;
this._globalCacheControl = options.globalCacheControl;
this._encryption = options.ServerSideEncryption;

const s3Options = {
params: { Bucket: this._bucket },
region: this._region,
signatureVersion: this._signatureVersion,
globalCacheControl: this._globalCacheControl,
};

if (options.accessKey && options.secretKey) {
awsCredentialsDeprecationNotice();
s3Options.accessKeyId = options.accessKey;
s3Options.secretAccessKey = options.secretKey;
}
const s3Options = {
params: { Bucket: this._bucket },
region: this._region,
signatureVersion: this._signatureVersion,
globalCacheControl: this._globalCacheControl,
};

Object.assign(s3Options, options.s3overrides);
if (options.accessKey && options.secretKey) {
awsCredentialsDeprecationNotice();
s3Options.accessKeyId = options.accessKey;
s3Options.secretAccessKey = options.secretKey;
}

this._s3Client = new AWS.S3(s3Options);
this._hasBucket = false;
}
Object.assign(s3Options, options.s3overrides);

S3Adapter.prototype.createBucket = function () {
let promise;
if (this._hasBucket) {
promise = Promise.resolve();
} else {
promise = new Promise((resolve) => {
this._s3Client.createBucket(() => {
this._hasBucket = true;
resolve();
});
});
this._s3Client = new AWS.S3(s3Options);
this._hasBucket = false;
}
return promise;
};

// For a given config object, filename, and data, store a file in S3
// Returns a promise containing the S3 object creation response
S3Adapter.prototype.createFile = function (filename, data, contentType) {
const params = {
Key: this._bucketPrefix + filename,
Body: data,
};
if (this._directAccess) {
params.ACL = 'public-read';
}
if (contentType) {
params.ContentType = contentType;
}
if (this._globalCacheControl) {
params.CacheControl = this._globalCacheControl;
}
if (this._encryption === 'AES256' || this._encryption === 'aws:kms') {
params.ServerSideEncryption = this._encryption;
createBucket() {
let promise;
if (this._hasBucket) {
promise = Promise.resolve();
} else {
promise = new Promise((resolve) => {
this._s3Client.createBucket(() => {
this._hasBucket = true;
resolve();
});
});
}
return promise;
}
return this.createBucket().then(() => new Promise((resolve, reject) => {
this._s3Client.upload(params, (err, response) => {
if (err !== null) {
return reject(err);
}
return resolve(response);
});
}));
};

S3Adapter.prototype.deleteFile = function (filename) {
return this.createBucket().then(() => new Promise((resolve, reject) => {
// For a given config object, filename, and data, store a file in S3
// Returns a promise containing the S3 object creation response
createFile(filename, data, contentType) {
const params = {
Key: this._bucketPrefix + filename,
Body: data,
};
this._s3Client.deleteObject(params, (err, data) => {
if (err !== null) {
return reject(err);
}
return resolve(data);
});
}));
};
if (this._directAccess) {
params.ACL = 'public-read';
}
if (contentType) {
params.ContentType = contentType;
}
if (this._globalCacheControl) {
params.CacheControl = this._globalCacheControl;
}
if (this._encryption === 'AES256' || this._encryption === 'aws:kms') {
params.ServerSideEncryption = this._encryption;
}
return this.createBucket().then(() => new Promise((resolve, reject) => {
this._s3Client.upload(params, (err, response) => {
if (err !== null) {
return reject(err);
}
return resolve(response);
});
}));
}

// Search for and return a file if found by filename
// Returns a promise that succeeds with the buffer result from S3
S3Adapter.prototype.getFileData = function (filename) {
const params = { Key: this._bucketPrefix + filename };
return this.createBucket().then(() => new Promise((resolve, reject) => {
this._s3Client.getObject(params, (err, data) => {
if (err !== null) {
return reject(err);
}
// Something happened here...
if (data && !data.Body) {
return reject(data);
}
return resolve(data.Body);
});
}));
};
deleteFile(filename) {
return this.createBucket().then(() => new Promise((resolve, reject) => {
const params = {
Key: this._bucketPrefix + filename,
};
this._s3Client.deleteObject(params, (err, data) => {
if (err !== null) {
return reject(err);
}
return resolve(data);
});
}));
}

// Generates and returns the location of a file stored in S3 for the given request and filename
// The location is the direct S3 link if the option is set,
// otherwise we serve the file through parse-server
S3Adapter.prototype.getFileLocation = function (config, filename) {
const fileName = encodeURIComponent(filename);
if (this._directAccess) {
if (this._baseUrl && this._baseUrlDirect) {
return `${this._baseUrl}/${fileName}`;
} if (this._baseUrl) {
return `${this._baseUrl}/${this._bucketPrefix + fileName}`;
}
return `https://${this._bucket}.s3.amazonaws.com/${this._bucketPrefix + fileName}`;
// Search for and return a file if found by filename
// Returns a promise that succeeds with the buffer result from S3
getFileData(filename) {
const params = { Key: this._bucketPrefix + filename };
return this.createBucket().then(() => new Promise((resolve, reject) => {
this._s3Client.getObject(params, (err, data) => {
if (err !== null) {
return reject(err);
}
// Something happened here...
if (data && !data.Body) {
return reject(data);
}
return resolve(data.Body);
});
}));
}
return (`${config.mount}/files/${config.applicationId}/${fileName}`);
};

S3Adapter.prototype.handleFileStream = function (filename, req, res) {
const params = {
Key: this._bucketPrefix + filename,
Range: req.get('Range'),
};
return this.createBucket().then(() => new Promise((resolve, reject) => {
this._s3Client.getObject(params, (error, data) => {
if (error !== null) {
return reject(error);
// Generates and returns the location of a file stored in S3 for the given request and filename
// The location is the direct S3 link if the option is set,
// otherwise we serve the file through parse-server
getFileLocation(config, filename) {
const fileName = encodeURIComponent(filename);
if (this._directAccess) {
if (this._baseUrl && this._baseUrlDirect) {
return `${this._baseUrl}/${fileName}`;
} if (this._baseUrl) {
return `${this._baseUrl}/${this._bucketPrefix + fileName}`;
}
if (data && !data.Body) {
return reject(data);
}
res.writeHead(206, {
'Accept-Ranges': data.AcceptRanges,
'Content-Length': data.ContentLength,
'Content-Range': data.ContentRange,
'Content-Type': data.ContentType,
return `https://${this._bucket}.s3.amazonaws.com/${this._bucketPrefix + fileName}`;
}
return (`${config.mount}/files/${config.applicationId}/${fileName}`);
}

handleFileStream(filename, req, res) {
const params = {
Key: this._bucketPrefix + filename,
Range: req.get('Range'),
};
return this.createBucket().then(() => new Promise((resolve, reject) => {
this._s3Client.getObject(params, (error, data) => {
if (error !== null) {
return reject(error);
}
if (data && !data.Body) {
return reject(data);
}
res.writeHead(206, {
'Accept-Ranges': data.AcceptRanges,
'Content-Length': data.ContentLength,
'Content-Range': data.ContentRange,
'Content-Type': data.ContentType,
});
res.write(data.Body);
res.end();
return resolve(data.Body);
});
res.write(data.Body);
res.end();
return resolve(data.Body);
});
}));
};
}));
}
}

module.exports = S3Adapter;
module.exports.default = S3Adapter;

0 comments on commit 83b12bd

Please sign in to comment.