Skip to content

Commit 0a982b7

Browse files
committed
Add some tests and demonstrate the adapter loading interface
1 parent 317d6ea commit 0a982b7

7 files changed

+183
-28
lines changed

spec/MockEmailAdapter.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
sendVerificationEmail: () => Promise.resolve();
3+
}

spec/MockEmailAdapterWithOptions.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = options => {
2+
if (!options) {
3+
throw "Options were not provided"
4+
}
5+
return {
6+
sendVerificationEmail: () => Promise.resolve()
7+
}
8+
}

spec/index.spec.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var request = require('request');
2+
var MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
23

34
describe('server', () => {
45
it('requires a master key and app id', done => {
@@ -36,4 +37,114 @@ describe('server', () => {
3637
done();
3738
});
3839
});
40+
41+
it('can load email adapter via object', done => {
42+
setServerConfiguration({
43+
serverURL: 'http://localhost:8378/1',
44+
appId: 'test',
45+
appName: 'unused',
46+
javascriptKey: 'test',
47+
dotNetKey: 'windows',
48+
clientKey: 'client',
49+
restAPIKey: 'rest',
50+
masterKey: 'test',
51+
collectionPrefix: 'test_',
52+
fileKey: 'test',
53+
verifyUserEmails: true,
54+
emailAdapter: MockEmailAdapterWithOptions({
55+
apiKey: 'k',
56+
domain: 'd',
57+
}),
58+
});
59+
done();
60+
});
61+
62+
it('can load email adapter via class', done => {
63+
setServerConfiguration({
64+
serverURL: 'http://localhost:8378/1',
65+
appId: 'test',
66+
appName: 'unused',
67+
javascriptKey: 'test',
68+
dotNetKey: 'windows',
69+
clientKey: 'client',
70+
restAPIKey: 'rest',
71+
masterKey: 'test',
72+
collectionPrefix: 'test_',
73+
fileKey: 'test',
74+
verifyUserEmails: true,
75+
emailAdapter: {
76+
class: MockEmailAdapterWithOptions,
77+
options: {
78+
apiKey: 'k',
79+
domain: 'd',
80+
}
81+
},
82+
});
83+
done();
84+
});
85+
86+
it('can load email adapter via module name', done => {
87+
setServerConfiguration({
88+
serverURL: 'http://localhost:8378/1',
89+
appId: 'test',
90+
appName: 'unused',
91+
javascriptKey: 'test',
92+
dotNetKey: 'windows',
93+
clientKey: 'client',
94+
restAPIKey: 'rest',
95+
masterKey: 'test',
96+
collectionPrefix: 'test_',
97+
fileKey: 'test',
98+
verifyUserEmails: true,
99+
emailAdapter: {
100+
module: './Email/SimpleMailgunAdapter',
101+
options: {
102+
apiKey: 'k',
103+
domain: 'd',
104+
}
105+
},
106+
});
107+
done();
108+
});
109+
110+
it('can load email adapter via only module name', done => {
111+
expect(() => setServerConfiguration({
112+
serverURL: 'http://localhost:8378/1',
113+
appId: 'test',
114+
appName: 'unused',
115+
javascriptKey: 'test',
116+
dotNetKey: 'windows',
117+
clientKey: 'client',
118+
restAPIKey: 'rest',
119+
masterKey: 'test',
120+
collectionPrefix: 'test_',
121+
fileKey: 'test',
122+
verifyUserEmails: true,
123+
emailAdapter: './Email/SimpleMailgunAdapter',
124+
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
125+
done();
126+
});
127+
128+
it('throws if you initialize email adapter incorrecly', done => {
129+
expect(() => setServerConfiguration({
130+
serverURL: 'http://localhost:8378/1',
131+
appId: 'test',
132+
appName: 'unused',
133+
javascriptKey: 'test',
134+
dotNetKey: 'windows',
135+
clientKey: 'client',
136+
restAPIKey: 'rest',
137+
masterKey: 'test',
138+
collectionPrefix: 'test_',
139+
fileKey: 'test',
140+
verifyUserEmails: true,
141+
emailAdapter: {
142+
module: './Email/SimpleMailgunAdapter',
143+
options: {
144+
domain: 'd',
145+
}
146+
},
147+
})).toThrow('SimpleMailgunAdapter requires an API Key and domain.');
148+
done();
149+
});
39150
});

src/Adapters/AdapterLoader.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
export function loadAdapter(options, defaultAdapter) {
32
let adapter;
43

@@ -12,7 +11,7 @@ export function loadAdapter(options, defaultAdapter) {
1211
adapter = options.adapter;
1312
}
1413
}
15-
14+
1615
if (!adapter) {
1716
adapter = defaultAdapter;
1817
}
@@ -26,10 +25,12 @@ export function loadAdapter(options, defaultAdapter) {
2625
}
2726
}
2827
// From there it's either a function or an object
29-
// if it's an function, instanciate and pass the options
28+
// if it's an function, instanciate and pass the options
3029
if (typeof adapter === "function") {
3130
var Adapter = adapter;
3231
adapter = new Adapter(options);
3332
}
3433
return adapter;
3534
}
35+
36+
module.exports = { loadAdapter }

src/Adapters/Email/SimpleMailgunAdapter.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import Mailgun from 'mailgun-js';
22

3-
export default (mailgunOptions) => {
3+
let SimpleMailgunAdapter = mailgunOptions => {
4+
if (!mailgunOptions || !mailgunOptions.apiKey || !mailgunOptions.domain) {
5+
throw 'SimpleMailgunAdapter requires an API Key and domain.';
6+
}
47
let mailgun = Mailgun(mailgunOptions);
58

69
let sendMail = (to, subject, text) => {
@@ -32,3 +35,5 @@ export default (mailgunOptions) => {
3235
}
3336
}
3437
}
38+
39+
module.exports = SimpleMailgunAdapter

src/Adapters/loadAdapter.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export default options => {
2+
if (!options) {
3+
return undefined;
4+
}
5+
6+
if (typeof options === 'string') {
7+
//Configuring via module name with no options
8+
return require(options)();
9+
}
10+
11+
if (!options.module && !options.class) {
12+
//Configuring via object
13+
return options;
14+
}
15+
16+
if (options.module) {
17+
//Configuring via module name + options
18+
return require(options.module)(options.options)
19+
}
20+
21+
if (options.class) {
22+
//Configuring via class + options
23+
return options.class(options.options);
24+
}
25+
}

src/index.js

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ var batch = require('./batch'),
1515
import ParsePushAdapter from './Adapters/Push/ParsePushAdapter';
1616
//import passwordReset from './passwordReset';
1717
import PromiseRouter from './PromiseRouter';
18-
import SimpleMailgunAdapter from './Adapters/Email/SimpleMailgunAdapter';
1918
import verifyEmail from './verifyEmail';
19+
import loadAdapter from './Adapters/loadAdapter';
2020
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
2121
import { ClassesRouter } from './Routers/ClassesRouter';
2222
import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter';
@@ -26,7 +26,7 @@ import { FunctionsRouter } from './Routers/FunctionsRouter';
2626
import { GridStoreAdapter } from './Adapters/Files/GridStoreAdapter';
2727
import { IAPValidationRouter } from './Routers/IAPValidationRouter';
2828
import { InstallationsRouter } from './Routers/InstallationsRouter';
29-
import { loadAdapter } from './Adapters/AdapterLoader';
29+
import AdapterLoader from './Adapters/AdapterLoader';
3030
import { LoggerController } from './Controllers/LoggerController';
3131
import { LogsRouter } from './Routers/LogsRouter';
3232
import { PushController } from './Controllers/PushController';
@@ -64,6 +64,20 @@ addParseCloud();
6464
// "javascriptKey": optional key from Parse dashboard
6565
// "push": optional key from configure push
6666

67+
let validateEmailConfiguration = (verifyUserEmails, appName, emailAdapter) => {
68+
if (verifyUserEmails) {
69+
if (typeof appName !== 'string') {
70+
throw 'An app name is required when using email verification.';
71+
}
72+
if (!emailAdapter) {
73+
throw 'User email verification was enabled, but no email adapter was provided';
74+
}
75+
if (typeof emailAdapter.sendVerificationEmail !== 'function') {
76+
throw 'Invalid email adapter: no sendVerificationEmail() function was provided';
77+
}
78+
}
79+
}
80+
6781
function ParseServer({
6882
appId,
6983
appName,
@@ -109,27 +123,11 @@ function ParseServer({
109123
}
110124
}
111125

112-
const filesControllerAdapter = loadAdapter(filesAdapter, GridStoreAdapter);
113-
const pushControllerAdapter = loadAdapter(push, ParsePushAdapter);
114-
const loggerControllerAdapter = loadAdapter(loggerAdapter, FileLoggerAdapter);
115-
116126
// We pass the options and the base class for the adatper,
117127
// Note that passing an instance would work too
118-
const filesController = new FilesController(filesControllerAdapter);
119-
const pushController = new PushController(pushControllerAdapter);
120-
const loggerController = new LoggerController(loggerControllerAdapter);
121-
122-
if (verifyUserEmails) {
123-
if (typeof appName !== 'string') {
124-
throw 'An app name is required when using email verification.';
125-
}
126-
if (!emailAdapter) {
127-
throw 'User email verification was enabled, but no email adapter was provided';
128-
}
129-
if (typeof emailAdapter.sendVerificationEmail !== 'function') {
130-
throw 'Invalid email adapter: no sendVerificationEmail() function was provided';
131-
}
132-
}
128+
const filesController = new FilesController(AdapterLoader.loadAdapter(filesAdapter, GridStoreAdapter));
129+
const pushController = new PushController(AdapterLoader.loadAdapter(push, ParsePushAdapter));
130+
const loggerController = new LoggerController(AdapterLoader.loadAdapter(loggerAdapter, FileLoggerAdapter));
133131

134132
cache.apps[appId] = {
135133
masterKey: masterKey,
@@ -145,11 +143,16 @@ function ParseServer({
145143
loggerController: loggerController,
146144
enableAnonymousUsers: enableAnonymousUsers,
147145
oauth: oauth,
148-
verifyUserEmails: verifyUserEmails,
149-
emailAdapter: emailAdapter,
150146
appName: appName,
151147
};
152148

149+
if (verifyUserEmails && process.env.PARSE_EXPERIMENTAL_EMAIL_VERIFICATION_ENABLED || process.env.TESTING == 1) {
150+
emailAdapter = loadAdapter(emailAdapter);
151+
validateEmailConfiguration(verifyUserEmails, appName, emailAdapter);
152+
cache.apps[appId].verifyUserEmails = verifyUserEmails;
153+
cache.apps[appId].emailAdapter = emailAdapter;
154+
}
155+
153156
// To maintain compatibility. TODO: Remove in some version that breaks backwards compatability
154157
if (process.env.FACEBOOK_APP_ID) {
155158
cache.apps[appId]['facebookAppIds'].push(process.env.FACEBOOK_APP_ID);
@@ -267,5 +270,4 @@ function getClassName(parseClass) {
267270
module.exports = {
268271
ParseServer: ParseServer,
269272
S3Adapter: S3Adapter,
270-
SimpleMailgunAdapter: SimpleMailgunAdapter,
271273
};

0 commit comments

Comments
 (0)