From 83b12bd58f937cd7013208cfea4e8176123cdf48 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Wed, 11 Sep 2019 13:10:05 -0500 Subject: [PATCH] Conform to FilesAdapter Interface (#73) * Conform to FilesAdapter Interface https://github.com/parse-community/parse-server/blob/master/src/Adapters/Files/FilesAdapter.js * Update .travis.yml --- .eslintrc.json | 1 - .travis.yml | 5 + index.js | 270 +++++++++++++++++++++++++------------------------ 3 files changed, 141 insertions(+), 135 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 1761d48..825b4c5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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 } diff --git a/.travis.yml b/.travis.yml index 01c98d3..68de45b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/index.js b/index.js index 9cc4d61..57375f7 100644 --- a/index.js +++ b/index.js @@ -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;