Skip to content

Commit b6f272a

Browse files
authored
Merge pull request #18 from wlky/patch-1
Allow to supply user object when sending via cloud code
2 parents c9cde83 + 4c2fab8 commit b6f272a

File tree

5 files changed

+102
-17
lines changed

5 files changed

+102
-17
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
# main
44
[Full Changelog](https://github.com/mtrezza/parse-server-api-mail-adapter/compare/1.0.4...master)
55

6+
### Breaking Changes
7+
*(none)*
8+
9+
### Notable Changes
10+
*(none)*
11+
12+
### Other Changes
13+
- Fixes undefined `user` in `localeCallback` when sending email via `Parse.Cloud.sendEmail()` (wlky, Manuel Trezza) [#18](https://github.com/mtrezza/parse-server-api-mail-adapter/pull/18)
14+
615
# 1.0.4
716
[Full Changelog](https://github.com/mtrezza/parse-server-api-mail-adapter/compare/1.0.3...1.0.4)
817

README.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ The Parse Server API Mail Adapter enables Parse Server to send emails using any
1818
- [Placeholders](#placeholders)
1919
- [Password Reset and Email Verification](#password-reset-and-email-verification)
2020
- [Localization](#localization)
21+
- [Cloud Code](#cloud-code)
22+
- [Example](#example)
23+
- [Parameters](#parameters)
2124
- [Need help?](#need-help)
2225

2326
# Installation
@@ -48,7 +51,7 @@ You can modify the script to use any other API you like or debug-step through th
4851
4952
# Configuration
5053
51-
An example configuation to add the API Mail Adapter to Parse Server could look like this:
54+
An example configuration to add the API Mail Adapter to Parse Server could look like this:
5255
5356
```js
5457
// Declare a mail client
@@ -188,6 +191,38 @@ Files are matched with the user locale in the following order:
188191
2. **Language** (locale `de-AT` matches file in folder `de` if there is no file in folder `de-AT`)
189192
3. **Default** (default file in base folder is returned if there is no file in folders `de-AT` and `de`)
190193

194+
# Cloud Code
195+
196+
Sending an email directly from Cloud Code is possible since Parse Server > 4.5.0. This adapter supports this convenience method.
197+
198+
## Example
199+
200+
If the `user` provided has an email address set, it is not necessary to set a `recipient` because the mail adapter will by default use the mail address of the `user`.
201+
202+
```js
203+
Parse.Cloud.sendEmail({
204+
templateName: "next_level_email",
205+
placeholders: { gameScore: 100, nextLevel: 2 },
206+
user: parseUser // user with email address
207+
});
208+
```
209+
210+
## Parameters
211+
212+
| Parameter | Type | Optional | Default Value | Example Value | Description |
213+
|----------------|--------------|----------|---------------|-----------------------------|------------------------------------------------------------------------------------------------|
214+
| `sender` | `String` | | - | `[email protected]` | The email sender address; overrides the sender address specified in the adapter configuration. |
215+
| `recipient` | `String` | | - | `[email protected]` | The email recipient; if set overrides the email address of the `user`. |
216+
| `subject` | `String` | | - | `Welcome` | The email subject. |
217+
| `text` | `String` | | - | `Thank you for signing up!` | The plain-text email content. |
218+
| `html` | `String` | yes | `undefined` | `<html>...</html>` | The HTML email content. |
219+
| `templateName` | `String` | yes | `undefined` | `customTemplate` | The template name. |
220+
| `placeholders` | `Object` | yes | `{}` | `{ key: value }` | The template placeholders. |
221+
| `extra` | `Object` | yes | `{}` | `{ key: value }` | Any additional variables to pass to the mail provider API. |
222+
| `user` | `Parse.User` | yes | `undefined` | - | The Parse User that the is the recipient of the email. |
223+
224+
225+
191226
# Need help?
192227

193228
- Ask on StackOverflow using the [parse-server](https://stackoverflow.com/questions/tagged/parse-server) tag.

spec/ApiMailAdapter.spec.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,51 @@ describe('ApiMailAdapter', () => {
219219
},
220220
extra: {
221221
field: "ExampleExtra"
222-
}
222+
},
223+
user: undefined,
223224
};
224225
const expectedArguments = Object.assign({}, options, { direct: true });
225226

226227
await expectAsync(adapter.sendMail(options)).toBeResolved();
227228
expect(_sendMail.calls.all()[0].args[0]).toEqual(expectedArguments);
228229
});
230+
231+
it('passes user to callback when user is passed to sendMail()', async () => {
232+
const adapter = new ApiMailAdapter(config);
233+
const localeCallbackSpy = spyOn(config.templates.customEmailWithLocaleCallback, 'localeCallback').and.callThrough();
234+
const options = {
235+
templateName: 'customEmailWithLocaleCallback',
236+
user: new Parse.User(),
237+
};
238+
239+
await expectAsync(adapter.sendMail(options)).toBeResolved();
240+
expect(localeCallbackSpy.calls.all()[0].args[0].get('locale')).toBe(options.user.get('locale'));
241+
});
242+
243+
it('uses user email if no recipient is passed to sendMail()', async () => {
244+
const adapter = new ApiMailAdapter(config);
245+
const apiCallbackSpy = spyOn(adapter, 'apiCallback').and.callThrough();
246+
const options = {
247+
templateName: 'customEmailWithLocaleCallback',
248+
user: new Parse.User(),
249+
};
250+
251+
await expectAsync(adapter.sendMail(options)).toBeResolved();
252+
expect(apiCallbackSpy.calls.all()[0].args[0].payload.to).toBe(options.user.get('email'));
253+
});
254+
255+
it('overrides user email if recipient is passed to sendMail()', async () => {
256+
const adapter = new ApiMailAdapter(config);
257+
const apiCallbackSpy = spyOn(adapter, 'apiCallback').and.callThrough();
258+
const options = {
259+
recipient: '[email protected]',
260+
templateName: 'customEmailWithLocaleCallback',
261+
user: new Parse.User(),
262+
};
263+
264+
await expectAsync(adapter.sendMail(options)).toBeResolved();
265+
expect(apiCallbackSpy.calls.all()[0].args[0].payload.to).toBe(options.recipient);
266+
});
229267
});
230268

231269
describe('generate API payload', function () {

spec/helper.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
'use strict';
2-
31
// Simulate Parse User class
42
const Parse = {
53
User: class User {
64
get(key) {
75
switch (key) {
86
case 'username':
9-
return 'ExampleUsername'
7+
return 'ExampleUsername';
108
case 'email':
11-
9+
10+
case 'locale':
11+
return 'de-AT';
1212
}
1313
}
1414
}

src/ApiMailAdapter.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,19 @@ class ApiMailAdapter extends MailAdapter {
8383

8484
/**
8585
* @function sendMail
86-
* @desciption Sends an email.
86+
* @description Sends an email.
8787
* @param {String} [sender] The email from address.
88-
* @param {String} recipient The email recipient.
88+
* @param {String} recipient The email recipient; if set overrides the email address of the `user`.
8989
* @param {String} [subject] The email subject.
9090
* @param {String} [text] The plain-text email content.
9191
* @param {String} [html] The HTML email content.
9292
* @param {String} [templateName] The template name.
93-
* @param {String} [placeholders] The template placeholders.
94-
* @param {String} [extra] Any additional variables to pass to the mail provider API.
93+
* @param {Object} [placeholders] The template placeholders.
94+
* @param {Object} [extra] Any additional variables to pass to the mail provider API.
95+
* @param {Parse.User} [user] The Parse User that the is the recipient of the email.
9596
* @returns {Promise<Any>} The mail provider API response.
9697
*/
97-
async sendMail({ sender, recipient, subject, text, html, templateName, placeholders, extra }) {
98+
async sendMail({ sender, recipient, subject, text, html, templateName, placeholders, extra, user }) {
9899
return await this._sendMail({
99100
sender,
100101
recipient,
@@ -104,6 +105,7 @@ class ApiMailAdapter extends MailAdapter {
104105
templateName,
105106
placeholders,
106107
extra,
108+
user,
107109
direct: true
108110
});
109111
}
@@ -117,7 +119,9 @@ class ApiMailAdapter extends MailAdapter {
117119
async _sendMail(email) {
118120

119121
// Define parameters
120-
let user, message;
122+
let message;
123+
const user = email.user;
124+
const userEmail = user ? user.get('email') : undefined;
121125
const templateName = email.templateName;
122126

123127
// If template name is not set
@@ -144,7 +148,7 @@ class ApiMailAdapter extends MailAdapter {
144148
if (email.direct) {
145149

146150
// If recipient is not set
147-
if (!email.recipient) {
151+
if (!email.recipient && !userEmail) {
148152
throw Errors.Error.noRecipient;
149153
}
150154

@@ -155,7 +159,7 @@ class ApiMailAdapter extends MailAdapter {
155159
message = Object.assign(
156160
{
157161
from: email.sender || this.sender,
158-
to: email.recipient,
162+
to: email.recipient || userEmail,
159163
subject: email.subject,
160164
text: email.text,
161165
html: email.html
@@ -166,20 +170,19 @@ class ApiMailAdapter extends MailAdapter {
166170
} else {
167171
// Get email parameters
168172
const { link, appName } = email;
169-
user = email.user;
170173

171174
// Add default placeholders for templates
172175
Object.assign(placeholders, {
173176
link,
174177
appName,
175-
email: user.get('email'),
178+
email: userEmail,
176179
username: user.get('username')
177180
});
178181

179182
// Set message properties
180183
message = {
181184
from: this.sender,
182-
to: user.get('email')
185+
to: userEmail
183186
};
184187
}
185188

0 commit comments

Comments
 (0)