Skip to content

Yet Another FileAdapter: Google Cloud Storage #708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ lib/

# cache folder
.cache

# Mac DS_Store files
.DS_Store
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,14 @@ PARSE_SERVER_MAX_UPLOAD_SIZE

```

##### Configuring S3 Adapter
##### Configuring File Adapters
Parse Server allows developers to choose from several options when hosting files: the `GridStoreAdapter`, which backed by MongoDB; the `S3Adapter`, which is backed by [Amazon S3](https://aws.amazon.com/s3/); or the `GCSAdapter`, which is backed by [Google Cloud Storage](https://cloud.google.com/storage/).

You can use the following environment variable setup the S3 adapter
`GridStoreAdapter` is used by default and requires no setup, but if you're interested in using S3 or GCS, additional configuration information is available below.

###### Configuring `S3Adapter`

You can use the following environment variable setup to enable the S3 adapter:

```js
S3_ACCESS_KEY
Expand All @@ -149,6 +154,19 @@ S3_DIRECT_ACCESS

```

###### Configuring `GCSAdapter`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great you added those too!


You can use the following environment variable setup to enable the GCS adapter:

```js
GCP_PROJECT_ID
GCP_KEYFILE_PATH
GCS_BUCKET
GCS_BUCKET_PREFIX
GCS_DIRECT_ACCESS

```

## Contributing

We really want Parse to be yours, to see it grow and thrive in the open source community. Please see the [Contributing to Parse Server guide](CONTRIBUTING.md).
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"commander": "^2.9.0",
"deepcopy": "^0.6.1",
"express": "^4.13.4",
"gcloud": "^0.28.0",
"mailgun-js": "^0.7.7",
"mime": "^1.3.4",
"mongodb": "~2.1.0",
Expand Down
40 changes: 25 additions & 15 deletions spec/AdapterLoader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,73 @@ var loadAdapter = require("../src/Adapters/AdapterLoader").loadAdapter;
var FilesAdapter = require("../src/Adapters/Files/FilesAdapter").default;
var ParsePushAdapter = require("../src/Adapters/Push/ParsePushAdapter");
var S3Adapter = require("../src/Adapters/Files/S3Adapter").default;
var GCSAdapter = require("../src/Adapters/Files/GCSAdapter").default;

describe("AdapterLoader", ()=>{

it("should instantiate an adapter from string in object", (done) => {
var adapterPath = require('path').resolve("./spec/MockAdapter");

var adapter = loadAdapter({
adapter: adapterPath,
options: {
key: "value",
key: "value",
foo: "bar"
}
});

expect(adapter instanceof Object).toBe(true);
expect(adapter.options.key).toBe("value");
expect(adapter.options.foo).toBe("bar");
done();
});

it("should instantiate an adapter from string", (done) => {
var adapterPath = require('path').resolve("./spec/MockAdapter");
var adapter = loadAdapter(adapterPath);

expect(adapter instanceof Object).toBe(true);
done();
});

it("should instantiate an adapter from string that is module", (done) => {
var adapterPath = require('path').resolve("./src/Adapters/Files/FilesAdapter");
var adapter = loadAdapter({
adapter: adapterPath
});

expect(adapter instanceof FilesAdapter).toBe(true);
done();
});

it("should instantiate an adapter from function/Class", (done) => {
var adapter = loadAdapter({
adapter: FilesAdapter
});
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});

it("should instantiate the default adapter from Class", (done) => {
var adapter = loadAdapter(null, FilesAdapter);
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});

it("should use the default adapter", (done) => {
var defaultAdapter = new FilesAdapter();
var adapter = loadAdapter(null, defaultAdapter);
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});

it("should use the provided adapter", (done) => {
var originalAdapter = new FilesAdapter();
var adapter = loadAdapter(originalAdapter);
expect(adapter).toBe(originalAdapter);
done();
});

it("should fail loading an improperly configured adapter", (done) => {
var Adapter = function(options) {
if (!options.foo) {
Expand All @@ -79,14 +80,14 @@ describe("AdapterLoader", ()=>{
param: "key",
doSomething: function() {}
};

expect(() => {
var adapter = loadAdapter(adapterOptions, Adapter);
expect(adapter).toEqual(adapterOptions);
}).not.toThrow("foo is required for that adapter");
done();
});

it("should load push adapter from options", (done) => {
var options = {
ios: {
Expand All @@ -100,7 +101,7 @@ describe("AdapterLoader", ()=>{
}).not.toThrow();
done();
});

it("should load S3Adapter from direct passing", (done) => {
var s3Adapter = new S3Adapter("key", "secret", "bucket")
expect(() => {
Expand All @@ -109,4 +110,13 @@ describe("AdapterLoader", ()=>{
}).not.toThrow();
done();
})

it("should load GCSAdapter from direct passing", (done) => {
var gcsAdapter = new GCSAdapter("projectId", "path/to/keyfile", "bucket")
expect(() => {
var adapter = loadAdapter(gcsAdapter, FilesAdapter);
expect(adapter).toBe(gcsAdapter);
}).not.toThrow();
done();
})
});
33 changes: 26 additions & 7 deletions spec/FilesController.spec.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,52 @@
var FilesController = require('../src/Controllers/FilesController').FilesController;
var GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
var S3Adapter = require("../src/Adapters/Files/S3Adapter").S3Adapter;
var GCSAdapter = require("../src/Adapters/Files/GCSAdapter").GCSAdapter;
var Config = require("../src/Config");

var FCTestFactory = require("./FilesControllerTestFactory");


// Small additional tests to improve overall coverage
describe("FilesController",()=>{

// Test the grid store adapter
var gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse');
FCTestFactory.testAdapter("GridStoreAdapter", gridStoreAdapter);

if (process.env.S3_ACCESS_KEY && process.env.S3_SECRET_KEY) {

// Test the S3 Adapter
var s3Adapter = new S3Adapter(process.env.S3_ACCESS_KEY, process.env.S3_SECRET_KEY, 'parse.server.tests');

FCTestFactory.testAdapter("S3Adapter",s3Adapter);

// Test S3 with direct access
var s3DirectAccessAdapter = new S3Adapter(process.env.S3_ACCESS_KEY, process.env.S3_SECRET_KEY, 'parse.server.tests', {
directAccess: true
});

FCTestFactory.testAdapter("S3AdapterDirect", s3DirectAccessAdapter);

} else if (!process.env.TRAVIS) {
console.log("set S3_ACCESS_KEY and S3_SECRET_KEY to test S3Adapter")
}

if (process.env.GCP_PROJECT_ID && process.env.GCP_KEYFILE_PATH && process.env.GCS_BUCKET) {

// Test the GCS Adapter
var gcsAdapter = new GCSAdapter(process.env.GCP_PROJECT_ID, process.env.GCP_KEYFILE_PATH, process.env.GCS_BUCKET);

FCTestFactory.testAdapter("GCSAdapter", gcsAdapter);

// Test GCS with direct access
var gcsDirectAccessAdapter = new GCSAdapter(process.env.GCP_PROJECT_ID, process.env.GCP_KEYFILE_PATH, process.env.GCS_BUCKET, {
directAccess: true
});

FCTestFactory.testAdapter("GCSAdapterDirect", gcsDirectAccessAdapter);

} else if (!process.env.TRAVIS) {
console.log("set GCP_PROJECT_ID, GCP_KEYFILE_PATH, and GCS_BUCKET to test GCSAdapter")
}
});
25 changes: 12 additions & 13 deletions spec/FilesControllerTestFactory.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@

var FilesController = require('../src/Controllers/FilesController').FilesController;
var Config = require("../src/Config");

var testAdapter = function(name, adapter) {
// Small additional tests to improve overall coverage

var config = new Config(Parse.applicationId);
var filesController = new FilesController(adapter);

describe("FilesController with "+name,()=>{

it("should properly expand objects", (done) => {

var result = filesController.expandFilesInObject(config, function(){});

expect(result).toBeUndefined();

var fullFile = {
type: '__type',
url: "http://an.url"
}

var anObject = {
aFile: fullFile
}
filesController.expandFilesInObject(config, anObject);
expect(anObject.aFile.url).toEqual("http://an.url");

done();
})
})

it("should properly create, read, delete files", (done) => {
var filename;
filesController.createFile(config, "file.txt", "hello world").then( (result) => {
Expand All @@ -51,14 +50,14 @@ var testAdapter = function(name, adapter) {
console.error(err);
done();
}).then((result) => {

filesController.getFileData(config, filename).then((res) => {
fail("the file should be deleted");
done();
}, (err) => {
done();
done();
});

}, (err) => {
fail("The adapter should delete the file");
console.error(err);
Expand Down
7 changes: 3 additions & 4 deletions src/Adapters/AdapterLoader.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export function loadAdapter(adapter, defaultAdapter, options) {

if (!adapter)
if (!adapter)
{
if (!defaultAdapter) {
return options;
Expand All @@ -20,7 +19,7 @@ export function loadAdapter(adapter, defaultAdapter, options) {
if (adapter.default) {
adapter = adapter.default;
}

return loadAdapter(adapter, undefined, options);
} else if (adapter.module) {
return loadAdapter(adapter.module, undefined, adapter.options);
Expand All @@ -30,7 +29,7 @@ export function loadAdapter(adapter, defaultAdapter, options) {
return loadAdapter(adapter.adapter, undefined, adapter.options);
}
// return the adapter as provided
return adapter;
return adapter;
}

export default loadAdapter;
Loading