Merge pull request #1144 from drew-gross/document-email-adapter

Document email adapter
This commit is contained in:
Drew
2016-03-24 19:31:45 -07:00
8 changed files with 89 additions and 85 deletions

View File

@@ -109,7 +109,7 @@ We have provided a basic [Node.js application](https://github.com/ParsePlatform/
* [Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-run-parse-server-on-ubuntu-14-04)
* [NodeChef](https://nodechef.com/blog/post/6/migrate-from-parse-to-nodechef%E2%80%99s-managed-parse-server)
* [Google App Engine](https://medium.com/@justinbeckwith/deploying-parse-server-to-google-app-engine-6bc0b7451d50)
* [Microsoft Azure](https://azure.microsoft.com/en-us/blog/azure-welcomes-parse-developers/)
* [Microsoft Azure](https://azure.microsoft.com/en-us/blog/azure-welcomes-parse-developers/)
* [Pivotal Web Services](https://github.com/cf-platform-eng/pws-parse-server)
* [Back4app](http://blog.back4app.com/2016/03/01/quick-wizard-migration/)
@@ -187,6 +187,36 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo
* `loggerAdapter` - The default behavior/transport (File) can be changed by creating an adapter class (see [`LoggerAdapter.js`](https://github.com/ParsePlatform/parse-server/blob/master/src/Adapters/Logger/LoggerAdapter.js)).
* `databaseAdapter` - The backing store can be changed by creating an adapter class (see `DatabaseAdapter.js`). Defaults to `MongoStorageAdapter`.
##### Email verification and password reset
Verifying user email addresses and enabling password reset via email requries an email adapter. As part of the `parse-server` package we provide an adapter for sending email through Mailgun. To use it, sign up for Mailgun, and add this to your initialization code:
```js
var server = ParseServer({
...otherOptions,
// Enable email verification
verifyUserEmails: true,
// The public URL of your app.
// This will appear in the link that is used to verify email addresses and reset passwords.
publicServerURL: 'https://example.com',
// Your apps name. This will appear in the subject and body of the emails that are sent.
appName: 'Parse App',
// The email adapter
emailAdapter: {
module: 'parse-server-simple-mailgun-adapter',
options: {
// The address that your emails come from
fromAddress: 'parse@example.com',
// Your domain from mailgun.com
domain: 'example.com',
// Your API key from mailgun.com
apiKey: 'key-mykey',
}
}
});
You can also use other email adapters contributed by the community such as [parse-server-sendgrid-adapter](https://www.npmjs.com/package/parse-server-sendgrid-adapter).
### Using environment variables to configure Parse Server
You may configure the Parse Server using environment variables:

View File

@@ -37,6 +37,7 @@
"parse-server-fs-adapter": "^1.0.0",
"parse-server-gcs-adapter": "^1.0.0",
"parse-server-s3-adapter": "^1.0.0",
"parse-server-simple-mailgun-adapter": "^1.0.0",
"redis": "^2.5.0-1",
"request": "^2.65.0",
"tv4": "^1.2.7",

View File

@@ -56,6 +56,7 @@ describe('server', () => {
fileKey: 'test',
verifyUserEmails: true,
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
@@ -80,6 +81,7 @@ describe('server', () => {
emailAdapter: {
class: MockEmailAdapterWithOptions,
options: {
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}
@@ -103,8 +105,9 @@ describe('server', () => {
fileKey: 'test',
verifyUserEmails: true,
emailAdapter: {
module: './Email/SimpleMailgunAdapter',
module: 'parse-server-simple-mailgun-adapter',
options: {
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}
@@ -127,9 +130,9 @@ describe('server', () => {
collectionPrefix: 'test_',
fileKey: 'test',
verifyUserEmails: true,
emailAdapter: './Email/SimpleMailgunAdapter',
emailAdapter: 'parse-server-simple-mailgun-adapter',
publicServerURL: 'http://localhost:8378/1'
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
})).toThrow('SimpleMailgunAdapter requires an API Key, domain, and fromAddress.');
done();
});
@@ -147,13 +150,13 @@ describe('server', () => {
fileKey: 'test',
verifyUserEmails: true,
emailAdapter: {
module: './Email/SimpleMailgunAdapter',
module: 'parse-server-simple-mailgun-adapter',
options: {
domain: 'd',
}
},
publicServerURL: 'http://localhost:8378/1'
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
})).toThrow('SimpleMailgunAdapter requires an API Key, domain, and fromAddress.');
done();
});

View File

@@ -10,8 +10,12 @@ export function loadAdapter(adapter, defaultAdapter, options) {
try {
return adapter(options);
} catch(e) {
var Adapter = adapter;
return new Adapter(options);
if (e.name === 'TypeError') {
var Adapter = adapter;
return new Adapter(options);
} else {
throw e;
}
}
} else if (typeof adapter === "string") {
adapter = require(adapter);
@@ -19,7 +23,6 @@ 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);

View File

@@ -1,32 +0,0 @@
import Mailgun from 'mailgun-js';
let SimpleMailgunAdapter = mailgunOptions => {
if (!mailgunOptions || !mailgunOptions.apiKey || !mailgunOptions.domain) {
throw 'SimpleMailgunAdapter requires an API Key and domain.';
}
let mailgun = Mailgun(mailgunOptions);
let sendMail = ({to, subject, text}) => {
let data = {
from: mailgunOptions.fromAddress,
to: to,
subject: subject,
text: text,
}
return new Promise((resolve, reject) => {
mailgun.messages().send(data, (err, body) => {
if (typeof err !== 'undefined') {
reject(err);
}
resolve(body);
});
});
}
return Object.freeze({
sendMail: sendMail
});
}
module.exports = SimpleMailgunAdapter

View File

@@ -1,8 +1,8 @@
import { randomString } from '../cryptoUtils';
import { inflate } from '../triggers';
import { randomString } from '../cryptoUtils';
import { inflate } from '../triggers';
import AdaptableController from './AdaptableController';
import MailAdapter from '../Adapters/Email/MailAdapter';
import rest from '../rest';
import MailAdapter from '../Adapters/Email/MailAdapter';
import rest from '../rest';
var DatabaseAdapter = require('../DatabaseAdapter');
var RestWrite = require('../RestWrite');
@@ -181,7 +181,7 @@ export class UserController extends AdaptableController {
defaultVerificationEmail({link, user, appName, }) {
let text = "Hi,\n\n" +
"You are being asked to confirm the e-mail address " + user.email + " with " + appName + "\n\n" +
"You are being asked to confirm the e-mail address " + user.get("email") + " with " + appName + "\n\n" +
"" +
"Click here to confirm it:\n" + link;
let to = user.get("email");

View File

@@ -11,41 +11,40 @@ var batch = require('./batch'),
Parse = require('parse/node').Parse,
authDataManager = require('./authDataManager');
//import passwordReset from './passwordReset';
import cache from './cache';
import Config from './Config';
import parseServerPackage from '../package.json';
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
import PromiseRouter from './PromiseRouter';
import requiredParameter from './requiredParameter';
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
import { ClassesRouter } from './Routers/ClassesRouter';
import { FeaturesRouter } from './Routers/FeaturesRouter';
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
import { FilesController } from './Controllers/FilesController';
import { FilesRouter } from './Routers/FilesRouter';
import { FunctionsRouter } from './Routers/FunctionsRouter';
import { GlobalConfigRouter } from './Routers/GlobalConfigRouter';
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
import { HooksController } from './Controllers/HooksController';
import { HooksRouter } from './Routers/HooksRouter';
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
import { InstallationsRouter } from './Routers/InstallationsRouter';
import { loadAdapter } from './Adapters/AdapterLoader';
import { LiveQueryController } from './Controllers/LiveQueryController';
import { LoggerController } from './Controllers/LoggerController';
import { LogsRouter } from './Routers/LogsRouter';
import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer';
import { PublicAPIRouter } from './Routers/PublicAPIRouter';
import { PushController } from './Controllers/PushController';
import { PushRouter } from './Routers/PushRouter';
import { randomString } from './cryptoUtils';
import { RolesRouter } from './Routers/RolesRouter';
import { SchemasRouter } from './Routers/SchemasRouter';
import { SessionsRouter } from './Routers/SessionsRouter';
import { setFeature } from './features';
import { UserController } from './Controllers/UserController';
import { UsersRouter } from './Routers/UsersRouter';
import cache from './cache';
import Config from './Config';
import parseServerPackage from '../package.json';
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
import PromiseRouter from './PromiseRouter';
import requiredParameter from './requiredParameter';
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
import { ClassesRouter } from './Routers/ClassesRouter';
import { FeaturesRouter } from './Routers/FeaturesRouter';
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
import { FilesController } from './Controllers/FilesController';
import { FilesRouter } from './Routers/FilesRouter';
import { FunctionsRouter } from './Routers/FunctionsRouter';
import { GlobalConfigRouter } from './Routers/GlobalConfigRouter';
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
import { HooksController } from './Controllers/HooksController';
import { HooksRouter } from './Routers/HooksRouter';
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
import { InstallationsRouter } from './Routers/InstallationsRouter';
import { loadAdapter } from './Adapters/AdapterLoader';
import { LiveQueryController } from './Controllers/LiveQueryController';
import { LoggerController } from './Controllers/LoggerController';
import { LogsRouter } from './Routers/LogsRouter';
import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer';
import { PublicAPIRouter } from './Routers/PublicAPIRouter';
import { PushController } from './Controllers/PushController';
import { PushRouter } from './Routers/PushRouter';
import { randomString } from './cryptoUtils';
import { RolesRouter } from './Routers/RolesRouter';
import { SchemasRouter } from './Routers/SchemasRouter';
import { SessionsRouter } from './Routers/SessionsRouter';
import { setFeature } from './features';
import { UserController } from './Controllers/UserController';
import { UsersRouter } from './Routers/UsersRouter';
// Mutate the Parse object to add the Cloud Code handlers
addParseCloud();

View File

@@ -1,7 +1,7 @@
import ParseServer from './ParseServer'
import { GCSAdapter } from 'parse-server-gcs-adapter';
import { S3Adapter } from 'parse-server-s3-adapter';
import { FileSystemAdapter } from 'parse-server-fs-adapter';
import ParseServer from './ParseServer';
import { GCSAdapter } from 'parse-server-gcs-adapter';
import { S3Adapter } from 'parse-server-s3-adapter';
import { FileSystemAdapter } from 'parse-server-fs-adapter';
// Factory function
let _ParseServer = function(options) {