Skip to content

Commit 29d70a6

Browse files
committed
Merge pull request #333 from ParsePlatform/nlutsenko.files.s3
Cleanup and modernize S3Adapter to ES6 syntax.
2 parents ddd40b2 + 07c9c1d commit 29d70a6

File tree

6 files changed

+96
-89
lines changed

6 files changed

+96
-89
lines changed

src/FilesAdapter.js renamed to src/Adapters/Files/FilesAdapter.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
// Allows you to change the file storage mechanism.
44
//
55
// Adapter classes must implement the following functions:
6-
// * createFileAsync(config, filename, data)
7-
// * getFileDataAsync(config, filename)
6+
// * createFile(config, filename, data)
7+
// * getFileData(config, filename)
88
// * getFileLocation(config, request, filename)
99
//
1010
// Default is GridStoreAdapter, which requires mongo
1111
// and for the API server to be using the ExportAdapter
1212
// database adapter.
1313

1414
export class FilesAdapter {
15-
createFileAsync(config, filename, data) { }
15+
createFile(config, filename, data) { }
1616

17-
getFileDataAsync(config, filename) { }
17+
getFileData(config, filename) { }
1818

1919
getFileLocation(config, filename) { }
2020
}

src/GridStoreAdapter.js renamed to src/Adapters/Files/GridStoreAdapter.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import { GridStore } from 'mongodb';
77
import { FilesAdapter } from './FilesAdapter';
88

9-
class GridStoreAdapter extends FilesAdapter {
9+
export class GridStoreAdapter extends FilesAdapter {
1010
// For a given config object, filename, and data, store a file
1111
// Returns a promise
12-
createFileAsync(config, filename, data) {
12+
createFile(config, filename, data) {
1313
return config.database.connect().then(() => {
1414
let gridStore = new GridStore(config.database.db, filename, 'w');
1515
return gridStore.open();
@@ -20,7 +20,7 @@ class GridStoreAdapter extends FilesAdapter {
2020
});
2121
}
2222

23-
getFileDataAsync(config, filename) {
23+
getFileData(config, filename) {
2424
return config.database.connect().then(() => {
2525
return GridStore.exist(config.database.db, filename);
2626
}).then(() => {

src/Adapters/Files/S3Adapter.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// S3Adapter
2+
//
3+
// Stores Parse files in AWS S3.
4+
5+
import * as AWS from 'aws-sdk';
6+
import { FilesAdapter } from './FilesAdapter';
7+
8+
const DEFAULT_S3_REGION = "us-east-1";
9+
const DEFAULT_S3_BUCKET = "parse-files";
10+
11+
export class S3Adapter extends FilesAdapter {
12+
// Creates an S3 session.
13+
// Providing AWS access and secret keys is mandatory
14+
// Region and bucket will use sane defaults if omitted
15+
constructor(
16+
accessKey,
17+
secretKey,
18+
{ region = DEFAULT_S3_REGION,
19+
bucket = DEFAULT_S3_BUCKET,
20+
bucketPrefix = '',
21+
directAccess = false } = {}
22+
) {
23+
super();
24+
25+
this._region = region;
26+
this._bucket = bucket;
27+
this._bucketPrefix = bucketPrefix;
28+
this._directAccess = directAccess;
29+
30+
let s3Options = {
31+
accessKeyId: accessKey,
32+
secretAccessKey: secretKey,
33+
params: { Bucket: this._bucket }
34+
};
35+
AWS.config._region = this._region;
36+
this._s3Client = new AWS.S3(s3Options);
37+
}
38+
39+
// For a given config object, filename, and data, store a file in S3
40+
// Returns a promise containing the S3 object creation response
41+
createFile(config, filename, data) {
42+
let params = {
43+
Key: this._bucketPrefix + filename,
44+
Body: data
45+
};
46+
if (this._directAccess) {
47+
params.ACL = "public-read"
48+
}
49+
return new Promise((resolve, reject) => {
50+
this._s3Client.upload(params, (err, data) => {
51+
if (err !== null) {
52+
return reject(err);
53+
}
54+
resolve(data);
55+
});
56+
});
57+
}
58+
59+
// Search for and return a file if found by filename
60+
// Returns a promise that succeeds with the buffer result from S3
61+
getFileData(config, filename) {
62+
let params = {Key: this._bucketPrefix + filename};
63+
return new Promise((resolve, reject) => {
64+
this._s3Client.getObject(params, (err, data) => {
65+
if (err !== null) {
66+
return reject(err);
67+
}
68+
resolve(data.Body);
69+
});
70+
});
71+
}
72+
73+
// Generates and returns the location of a file stored in S3 for the given request and filename
74+
// The location is the direct S3 link if the option is set, otherwise we serve the file through parse-server
75+
getFileLocation(config, filename) {
76+
if (this._directAccess) {
77+
return ('https://' + this.bucket + '._s3Client.amazonaws.com' + '/' + this._bucketPrefix + filename);
78+
}
79+
return (config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename));
80+
}
81+
}
82+
83+
export default S3Adapter;

src/Controllers/FilesController.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class FilesController {
1919
return (req, res) => {
2020
let config = new Config(req.params.appId);
2121
let filename = req.params.filename;
22-
this._filesAdapter.getFileDataAsync(config, filename).then((data) => {
22+
this._filesAdapter.getFileData(config, filename).then((data) => {
2323
res.status(200);
2424
var contentType = mime.lookup(filename);
2525
res.set('Content-type', contentType);
@@ -62,7 +62,7 @@ export class FilesController {
6262
}
6363

6464
let filename = rack() + '_' + req.params.filename + extension;
65-
this._filesAdapter.createFileAsync(req.config, filename, req.body).then(() => {
65+
this._filesAdapter.createFile(req.config, filename, req.body).then(() => {
6666
res.status(201);
6767
var location = this._filesAdapter.getFileLocation(req.config, filename);
6868
res.set('Location', location);

src/S3Adapter.js

Lines changed: 0 additions & 77 deletions
This file was deleted.

src/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ var batch = require('./batch'),
55
cache = require('./cache'),
66
DatabaseAdapter = require('./DatabaseAdapter'),
77
express = require('express'),
8-
S3Adapter = require('./S3Adapter'),
98
middlewares = require('./middlewares'),
109
multer = require('multer'),
1110
Parse = require('parse/node').Parse,
1211
PromiseRouter = require('./PromiseRouter'),
1312
httpRequest = require('./httpRequest');
1413

15-
import { default as GridStoreAdapter } from './GridStoreAdapter';
16-
import { default as FilesController } from './Controllers/FilesController';
14+
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
15+
import { S3Adapter } from './Adapters/Files/S3Adapter';
16+
17+
import { FilesController } from './Controllers/FilesController';
1718

1819
// Mutate the Parse object to add the Cloud Code handlers
1920
addParseCloud();

0 commit comments

Comments
 (0)