Skip to content

Commit 274f6ba

Browse files
authored
Merge pull request #51 from emailjs-com/block-emails
The block list option
2 parents cb318a3 + 867bc2b commit 274f6ba

File tree

14 files changed

+301
-18
lines changed

14 files changed

+301
-18
lines changed

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,22 @@ Options can be declared globally using the **init** method or locally as the fou
115115
\
116116
The local parameter will have higher priority than the global one.
117117

118-
| Name | Type | Default | Description |
119-
| ------------- | ------- | ------- | -------------------------------------------------------- |
120-
| publicKey | String | | The public key is required to invoke the method. |
121-
| blockHeadless | Boolean | False | Method will return error 451 if the browser is headless. |
118+
| Name | Type | Default | Description |
119+
| ------------- | --------- | ------- | -------------------------------------------------------- |
120+
| publicKey | String | | The public key is required to invoke the method. |
121+
| blockHeadless | Boolean | False | Method will return error 451 if the browser is headless. |
122+
| blockList | BlockList | | Block list settings. |
123+
124+
**BlockList**
125+
126+
Allows to ignore a method call if the watched variable contains a value from the block list.
127+
\
128+
Method will return error 403 if request is blocked.
129+
130+
| Name | Type | Description |
131+
| ------------- | -------- | -------------------------------------------------- |
132+
| list | String[] | The array of strings contains values for blocking. |
133+
| watchVariable | String | A name of the variable to be watched. |
122134

123135
**Declare global settings**
124136

@@ -128,6 +140,9 @@ import emailjs from '@emailjs/browser';
128140
emailjs.init({
129141
publicKey: 'YOUR_PUBLIC_KEY',
130142
blockHeadless: true,
143+
blockList: {
144+
list: ['foo@emailjs.com', 'bar@emailjs.com'],
145+
},
131146
});
132147
```
133148

@@ -144,6 +159,9 @@ const templateParams = {
144159
emailjs
145160
.send('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', templateParams, {
146161
publicKey: 'YOUR_PUBLIC_KEY',
162+
blockList: {
163+
watchVariable: 'userEmail',
164+
},
147165
})
148166
.then(
149167
(response) => {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { it, expect } from '@jest/globals';
2+
3+
import { EmailJSResponseStatus } from '../../models/EmailJSResponseStatus';
4+
import { blockedEmailError } from './blockedEmailError';
5+
6+
it('should return EmailJSResponseStatus', () => {
7+
expect(blockedEmailError()).toBeInstanceOf(EmailJSResponseStatus);
8+
});
9+
10+
it('should return status 403', () => {
11+
expect(blockedEmailError()).toEqual({
12+
status: 403,
13+
text: 'Forbidden',
14+
});
15+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { EmailJSResponseStatus } from '../../models/EmailJSResponseStatus';
2+
3+
export const blockedEmailError = () => {
4+
return new EmailJSResponseStatus(403, 'Forbidden');
5+
};

src/methods/init/init.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { store } from '../../store/store';
66
beforeEach(() => {
77
store.origin = 'https://api.emailjs.com';
88
store.limitRate = 0;
9-
store.blockList = [];
109
store.publicKey = undefined;
1110
});
1211

@@ -18,7 +17,6 @@ describe('sdk v3', () => {
1817
origin: 'https://api.emailjs.com',
1918
publicKey: 'C2JWGTestKeySomething',
2019
limitRate: 0,
21-
blockList: [],
2220
});
2321
});
2422
});
@@ -30,21 +28,24 @@ describe('sdk v4', () => {
3028
expect(store).toEqual({
3129
origin: 'https://api.emailjs.com',
3230
limitRate: 0,
33-
blockList: [],
3431
});
3532
});
3633

3734
it('should call the init method with custom options', () => {
3835
init({
3936
publicKey: 'C2JWGTestKeySomething',
40-
blockList: ['block@email.com'],
37+
blockList: {
38+
list: ['block@email.com'],
39+
},
4140
limitRate: 10000,
4241
});
4342

4443
expect(store).toEqual({
4544
origin: 'https://api.emailjs.com',
4645
publicKey: 'C2JWGTestKeySomething',
47-
blockList: ['block@email.com'],
46+
blockList: {
47+
list: ['block@email.com'],
48+
},
4849
limitRate: 10000,
4950
});
5051
});

src/methods/init/init.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const init = (
1818

1919
store.publicKey = opts.publicKey;
2020
store.blockHeadless = opts.blockHeadless;
21+
store.blockList = opts.blockList;
2122
store.limitRate = opts.limitRate || store.limitRate;
22-
store.blockList = opts.blockList || store.blockList;
2323
store.origin = opts.origin || origin;
2424
};

src/methods/send/send.spec.ts

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ describe('sdk v4', () => {
5555
).toThrow('The service ID is required');
5656
});
5757

58+
it('should call the send method and fail on the template ID', () => {
59+
expect(() =>
60+
send('default_service', '', undefined, {
61+
publicKey: 'C2JWGTestKeySomething',
62+
}),
63+
).toThrow('The template ID is required');
64+
});
65+
5866
it('should call the send method and fail on headless', async () => {
5967
try {
6068
const result = await send(
@@ -76,7 +84,7 @@ describe('sdk v4', () => {
7684
});
7785

7886
it('should call the send method and fail on headless as promise', () => {
79-
return send('', 'my_test_template', undefined, {
87+
return send('default_service', 'my_test_template', undefined, {
8088
publicKey: 'C2JWGTestKeySomething',
8189
blockHeadless: true,
8290
}).then(
@@ -92,12 +100,56 @@ describe('sdk v4', () => {
92100
);
93101
});
94102

95-
it('should call the send method and fail on the template ID', () => {
96-
expect(() =>
97-
send('default_service', '', undefined, {
103+
it('should call the send method and fail on blocklist', async () => {
104+
try {
105+
const result = await send(
106+
'default_service',
107+
'my_test_template',
108+
{
109+
email: 'bar@emailjs.com',
110+
},
111+
{
112+
publicKey: 'C2JWGTestKeySomething',
113+
blockList: {
114+
list: ['foo@emailjs.com', 'bar@emailjs.com'],
115+
watchVariable: 'email',
116+
},
117+
},
118+
);
119+
expect(result).toBeUndefined();
120+
} catch (error) {
121+
expect(error).toEqual({
122+
status: 403,
123+
text: 'Forbidden',
124+
});
125+
}
126+
});
127+
128+
it('should call the send method and fail on blocklist as promise', () => {
129+
return send(
130+
'default_service',
131+
'my_test_template',
132+
{
133+
email: 'bar@emailjs.com',
134+
},
135+
{
98136
publicKey: 'C2JWGTestKeySomething',
99-
}),
100-
).toThrow('The template ID is required');
137+
blockList: {
138+
list: ['foo@emailjs.com', 'bar@emailjs.com'],
139+
watchVariable: 'email',
140+
},
141+
},
142+
).then(
143+
(result) => {
144+
expect(result).toBeUndefined();
145+
},
146+
(error) => {
147+
expect(error).toEqual({
148+
status: 403,
149+
text: 'Forbidden',
150+
});
151+
},
152+
);
101153
});
102154

103155
it('should call the send method successfully with 4 params', async () => {

src/methods/send/send.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { validateParams } from '../../utils/validateParams/validateParams';
88
import { validateTemplateParams } from '../../utils/validateTemplateParams/validateTemplateParams';
99
import { isHeadless } from '../../utils/isHeadless/isHeadless';
1010
import { headlessError } from '../../errors/headlessError/headlessError';
11+
import { isBlockedValueInParams } from '../../utils/isBlockedValue/isBlockedValue';
12+
import { blockedEmailError } from '../../errors/blockedEmailError/blockedEmailError';
1113

1214
/**
1315
* Send a template to the specific EmailJS service
@@ -26,6 +28,7 @@ export const send = (
2628
const opts = buildOptions(options);
2729
const publicKey = opts.publicKey || store.publicKey;
2830
const blockHeadless = opts.blockHeadless || store.blockHeadless;
31+
const blockList = { ...store.blockList, ...opts.blockList };
2932

3033
if (blockHeadless && isHeadless(navigator)) {
3134
return Promise.reject(headlessError());
@@ -34,6 +37,10 @@ export const send = (
3437
validateParams(publicKey, serviceID, templateID);
3538
validateTemplateParams(templateParams);
3639

40+
if (templateParams && isBlockedValueInParams(blockList, templateParams)) {
41+
return Promise.reject(blockedEmailError());
42+
}
43+
3744
const params = {
3845
lib_version: process.env.npm_package_version,
3946
user_id: publicKey,

src/methods/sendForm/sendForm.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,63 @@ describe('sdk v4', () => {
126126
);
127127
});
128128

129+
it('should call the sendForm and fail on blocklist', async () => {
130+
const form: HTMLFormElement = document.createElement('form');
131+
const input: HTMLInputElement = document.createElement('input');
132+
133+
input.type = 'hidden';
134+
input.name = 'email';
135+
input.value = 'bar@emailjs.com';
136+
137+
form.append(input);
138+
139+
try {
140+
const result = await sendForm('default_service', 'my_test_template', form, {
141+
publicKey: 'C2JWGTestKeySomething',
142+
blockList: {
143+
list: ['foo@emailjs.com', 'bar@emailjs.com'],
144+
watchVariable: 'email',
145+
},
146+
});
147+
148+
expect(result).toBeUndefined();
149+
} catch (error) {
150+
expect(error).toEqual({
151+
status: 403,
152+
text: 'Forbidden',
153+
});
154+
}
155+
});
156+
157+
it('should call the sendForm and fail on blocklist as promise', () => {
158+
const form: HTMLFormElement = document.createElement('form');
159+
const input: HTMLInputElement = document.createElement('input');
160+
161+
input.type = 'hidden';
162+
input.name = 'email';
163+
input.value = 'bar@emailjs.com';
164+
165+
form.append(input);
166+
167+
return sendForm('default_service', 'my_test_template', form, {
168+
publicKey: 'C2JWGTestKeySomething',
169+
blockList: {
170+
list: ['foo@emailjs.com', 'bar@emailjs.com'],
171+
watchVariable: 'email',
172+
},
173+
}).then(
174+
(result) => {
175+
expect(result).toBeUndefined();
176+
},
177+
(error) => {
178+
expect(error).toEqual({
179+
status: 403,
180+
text: 'Forbidden',
181+
});
182+
},
183+
);
184+
});
185+
129186
it('should call the sendForm with id selector', async () => {
130187
const form: HTMLFormElement = document.createElement('form');
131188
form.id = 'form-id';
@@ -135,6 +192,7 @@ describe('sdk v4', () => {
135192
const result = await sendForm('default_service', 'my_test_template', '#form-id', {
136193
publicKey: 'C2JWGTestKeySomething',
137194
});
195+
138196
expect(result).toEqual({ status: 200, text: 'OK' });
139197
} catch (error) {
140198
expect(error).toBeUndefined();
@@ -148,6 +206,7 @@ describe('sdk v4', () => {
148206
const result = await sendForm('default_service', 'my_test_template', form, {
149207
publicKey: 'C2JWGTestKeySomething',
150208
});
209+
151210
expect(result).toEqual({ status: 200, text: 'OK' });
152211
} catch (error) {
153212
expect(error).toBeUndefined();

src/methods/sendForm/sendForm.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { validateForm } from '../../utils/validateForm/validateForm';
88
import { validateParams } from '../../utils/validateParams/validateParams';
99
import { isHeadless } from '../../utils/isHeadless/isHeadless';
1010
import { headlessError } from '../../errors/headlessError/headlessError';
11+
import { isBlockedValueInParams } from '../../utils/isBlockedValue/isBlockedValue';
12+
import { blockedEmailError } from '../../errors/blockedEmailError/blockedEmailError';
1113

1214
const findHTMLForm = (form: string | HTMLFormElement): HTMLFormElement | null => {
1315
return typeof form === 'string' ? document.querySelector<HTMLFormElement>(form) : form;
@@ -30,6 +32,7 @@ export const sendForm = (
3032
const opts = buildOptions(options);
3133
const publicKey = opts.publicKey || store.publicKey;
3234
const blockHeadless = opts.blockHeadless || store.blockHeadless;
35+
const blockList = { ...store.blockList, ...opts.blockList };
3336

3437
if (blockHeadless && isHeadless(navigator)) {
3538
return Promise.reject(headlessError());
@@ -41,6 +44,11 @@ export const sendForm = (
4144
validateForm(currentForm);
4245

4346
const formData: FormData = new FormData(currentForm!);
47+
48+
if (isBlockedValueInParams(blockList, formData)) {
49+
return Promise.reject(blockedEmailError());
50+
}
51+
4452
formData.append('lib_version', process.env.npm_package_version!);
4553
formData.append('service_id', serviceID);
4654
formData.append('template_id', templateID);

src/store/store.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ export const store: Options = {
44
origin: 'https://api.emailjs.com',
55
blockHeadless: false,
66
limitRate: 0,
7-
blockList: [],
87
};

0 commit comments

Comments
 (0)