@trunkrs/common 1.5.6 → 1.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/models/email/Attachment.d.ts +8 -0
- package/models/email/Attachment.js +3 -0
- package/models/email/Attachment.js.map +1 -0
- package/models/email/EmailContent.d.ts +6 -0
- package/models/email/EmailContent.js +3 -0
- package/models/email/EmailContent.js.map +1 -0
- package/models/email/Template.d.ts +7 -0
- package/models/email/Template.js +3 -0
- package/models/email/Template.js.map +1 -0
- package/models/email/index.d.ts +3 -0
- package/models/email/index.js +3 -0
- package/models/email/index.js.map +1 -0
- package/models/enum/Environment.d.ts +6 -0
- package/models/enum/Environment.js +10 -0
- package/models/enum/Environment.js.map +1 -0
- package/models/errors/email/EmailValidationError.d.ts +5 -0
- package/models/errors/email/EmailValidationError.js +14 -0
- package/models/errors/email/EmailValidationError.js.map +1 -0
- package/models/errors/email/TemplateNotFoundError.d.ts +5 -0
- package/models/errors/email/TemplateNotFoundError.js +14 -0
- package/models/errors/email/TemplateNotFoundError.js.map +1 -0
- package/models/errors/email/index.d.ts +2 -0
- package/models/errors/email/index.js +11 -0
- package/models/errors/email/index.js.map +1 -0
- package/package.json +4 -3
- package/providers/auth0.js +1 -0
- package/providers/auth0.js.map +1 -1
- package/providers/aws.js.map +1 -1
- package/providers/email.d.ts +17 -0
- package/providers/email.js +59 -0
- package/providers/email.js.map +1 -0
- package/services/aws/dynamodb/utils/operators/And.js +7 -2
- package/services/aws/dynamodb/utils/operators/And.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/BeginsWith.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/BeginsWith.js +3 -2
- package/services/aws/dynamodb/utils/operators/BeginsWith.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/Between.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/Between.js +3 -2
- package/services/aws/dynamodb/utils/operators/Between.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/Contains.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/Contains.js +3 -2
- package/services/aws/dynamodb/utils/operators/Contains.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/DynamoOperator.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/DynamoOperator.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/Exists.js +1 -1
- package/services/aws/dynamodb/utils/operators/Exists.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/GreaterThan.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/GreaterThan.js +3 -2
- package/services/aws/dynamodb/utils/operators/GreaterThan.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/GreaterThanOrEquals.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/GreaterThanOrEquals.js +3 -2
- package/services/aws/dynamodb/utils/operators/GreaterThanOrEquals.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/In.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/In.js +3 -2
- package/services/aws/dynamodb/utils/operators/In.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/LesserThan.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/LesserThan.js +3 -2
- package/services/aws/dynamodb/utils/operators/LesserThan.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/LesserThanOrEquals.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/LesserThanOrEquals.js +3 -2
- package/services/aws/dynamodb/utils/operators/LesserThanOrEquals.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/NotEquals.d.ts +1 -1
- package/services/aws/dynamodb/utils/operators/NotEquals.js +3 -2
- package/services/aws/dynamodb/utils/operators/NotEquals.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/NotExists.js +1 -1
- package/services/aws/dynamodb/utils/operators/NotExists.js.map +1 -1
- package/services/aws/dynamodb/utils/operators/Or.js +7 -2
- package/services/aws/dynamodb/utils/operators/Or.js.map +1 -1
- package/services/email/EmailClient/index.d.ts +10 -0
- package/services/email/EmailClient/index.js +39 -0
- package/services/email/EmailClient/index.js.map +1 -0
- package/services/email/EmailClient/models.d.ts +16 -0
- package/services/email/EmailClient/models.js +3 -0
- package/services/email/EmailClient/models.js.map +1 -0
- package/services/email/NodemailerEmailClient.d.ts +19 -0
- package/services/email/NodemailerEmailClient.js +55 -0
- package/services/email/NodemailerEmailClient.js.map +1 -0
- package/services/email/SESTemplateClient.d.ts +16 -0
- package/services/email/SESTemplateClient.js +62 -0
- package/services/email/SESTemplateClient.js.map +1 -0
- package/services/email/TemplateClient.d.ts +6 -0
- package/services/email/TemplateClient.js +12 -0
- package/services/email/TemplateClient.js.map +1 -0
- package/services/email/__tests__/NodemailerEmailClient.spec.d.ts +1 -0
- package/services/email/__tests__/NodemailerEmailClient.spec.js +83 -0
- package/services/email/__tests__/NodemailerEmailClient.spec.js.map +1 -0
- package/services/email/__tests__/SESTemplateClient.spec.d.ts +1 -0
- package/services/email/__tests__/SESTemplateClient.spec.js +55 -0
- package/services/email/__tests__/SESTemplateClient.spec.js.map +1 -0
- package/services/email/index.d.ts +4 -0
- package/services/email/index.js +15 -0
- package/services/email/index.js.map +1 -0
- package/utils/caching/GlobalAtomicCache.js +13 -0
- package/utils/caching/GlobalAtomicCache.js.map +1 -1
- package/utils/caching/SecretCache.js +7 -0
- package/utils/caching/SecretCache.js.map +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import DynamoOperator from './DynamoOperator';
|
|
2
2
|
declare class NotEquals extends DynamoOperator<number | string> {
|
|
3
3
|
constructor(valueToExclude: number | string);
|
|
4
|
-
render(attributeName: string): string;
|
|
4
|
+
render(attributeName: string, paramOffset?: number): string;
|
|
5
5
|
static fromValue(valueToExclude: number | string): NotEquals;
|
|
6
6
|
}
|
|
7
7
|
export default NotEquals;
|
|
@@ -8,8 +8,9 @@ class NotEquals extends DynamoOperator_1.default {
|
|
|
8
8
|
constructor(valueToExclude) {
|
|
9
9
|
super([valueToExclude]);
|
|
10
10
|
}
|
|
11
|
-
render(attributeName) {
|
|
12
|
-
|
|
11
|
+
render(attributeName, paramOffset) {
|
|
12
|
+
const actualOffset = paramOffset !== null && paramOffset !== void 0 ? paramOffset : 0;
|
|
13
|
+
return `#${attributeName} <> ${this.makeAttrValueName(attributeName, actualOffset)}`;
|
|
13
14
|
}
|
|
14
15
|
static fromValue(valueToExclude) {
|
|
15
16
|
return new NotEquals(valueToExclude);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotEquals.js","sourceRoot":"","sources":["../../../../../../services/aws/dynamodb/utils/operators/NotEquals.ts"],"names":[],"mappings":";;;;;AAAA,sEAA6C;AAE7C,MAAM,SAAU,SAAQ,wBAA+B;IACrD,YAAmB,cAA+B;QAChD,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,CAAC,aAAqB;
|
|
1
|
+
{"version":3,"file":"NotEquals.js","sourceRoot":"","sources":["../../../../../../services/aws/dynamodb/utils/operators/NotEquals.ts"],"names":[],"mappings":";;;;;AAAA,sEAA6C;AAE7C,MAAM,SAAU,SAAQ,wBAA+B;IACrD,YAAmB,cAA+B;QAChD,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,CAAC,aAAqB,EAAE,WAAoB;QAChD,MAAM,YAAY,GAAG,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,CAAC,CAAA;QACrC,OAAO,IAAI,aAAa,OAAO,IAAI,CAAC,iBAAiB,CACnD,aAAa,EACb,YAAY,CACb,EAAE,CAAA;IACL,CAAC;IAEM,MAAM,CAAC,SAAS,CAAC,cAA+B;QACrD,OAAO,IAAI,SAAS,CAAC,cAAc,CAAC,CAAA;IACtC,CAAC;CACF;AAED,kBAAe,SAAS,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotExists.js","sourceRoot":"","sources":["../../../../../../services/aws/dynamodb/utils/operators/NotExists.ts"],"names":[],"mappings":";;;;;AAAA,sEAA6C;AAE7C,MAAM,MAAO,SAAQ,wBAAc;IACjC;QACE,KAAK,CAAC,EAAE,CAAC,CAAA;IACX,CAAC;IAED,MAAM,CAAC,aAAqB;QAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"NotExists.js","sourceRoot":"","sources":["../../../../../../services/aws/dynamodb/utils/operators/NotExists.ts"],"names":[],"mappings":";;;;;AAAA,sEAA6C;AAE7C,MAAM,MAAO,SAAQ,wBAAc;IACjC;QACE,KAAK,CAAC,EAAE,CAAC,CAAA;IACX,CAAC;IAED,MAAM,CAAC,aAAqB;QAC1B,OAAO,yBAAyB,aAAa,GAAG,CAAA;IAClD,CAAC;CACF;AAED,kBAAe,MAAM,CAAA"}
|
|
@@ -6,12 +6,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const DynamoOperator_1 = __importDefault(require("./DynamoOperator"));
|
|
7
7
|
class Or extends DynamoOperator_1.default {
|
|
8
8
|
constructor(innerOperators) {
|
|
9
|
-
super(
|
|
9
|
+
super(innerOperators.flatMap((op) => op.attributeValues));
|
|
10
10
|
this.innerOperators = innerOperators;
|
|
11
11
|
}
|
|
12
12
|
render(attributeName) {
|
|
13
|
+
let offset = 0;
|
|
13
14
|
const clauseBody = this.innerOperators
|
|
14
|
-
.map((operator) =>
|
|
15
|
+
.map((operator) => {
|
|
16
|
+
const result = operator.render(attributeName, offset);
|
|
17
|
+
offset += operator.attributeValues.length;
|
|
18
|
+
return result;
|
|
19
|
+
})
|
|
15
20
|
.join(' OR ');
|
|
16
21
|
return `(${clauseBody})`;
|
|
17
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Or.js","sourceRoot":"","sources":["../../../../../../services/aws/dynamodb/utils/operators/Or.ts"],"names":[],"mappings":";;;;;AAAA,sEAA6C;AAE7C,MAAM,EAAG,SAAQ,wBAAc;IAC7B,YAAoC,cAAgC;QAClE,KAAK,CAAC,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"Or.js","sourceRoot":"","sources":["../../../../../../services/aws/dynamodb/utils/operators/Or.ts"],"names":[],"mappings":";;;;;AAAA,sEAA6C;AAE7C,MAAM,EAAG,SAAQ,wBAAc;IAC7B,YAAoC,cAAgC;QAClE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAA;QADvB,mBAAc,GAAd,cAAc,CAAkB;IAEpE,CAAC;IAED,MAAM,CAAC,aAAqB;QAC1B,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc;aACnC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;YAChB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;YACrD,MAAM,IAAI,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAA;YAEzC,OAAO,MAAM,CAAA;QACf,CAAC,CAAC;aACD,IAAI,CAAC,MAAM,CAAC,CAAA;QAEf,OAAO,IAAI,UAAU,GAAG,CAAA;IAC1B,CAAC;CACF;AAED,kBAAe,EAAE,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EmailClientConfig, SendTemplatedEmailRequest } from './models';
|
|
2
|
+
declare abstract class EmailClient<TConfig extends EmailClientConfig = EmailClientConfig> {
|
|
3
|
+
protected readonly config: TConfig;
|
|
4
|
+
protected constructor(config: TConfig);
|
|
5
|
+
sendTemplatedEmail<TValues>(params: SendTemplatedEmailRequest<TValues>): Promise<void>;
|
|
6
|
+
protected abstract sendEmail<TValues>(params: SendTemplatedEmailRequest<TValues>): Promise<void>;
|
|
7
|
+
private validateRecipientDomains;
|
|
8
|
+
private areRecipientDomainsInvalid;
|
|
9
|
+
}
|
|
10
|
+
export default EmailClient;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const EmailValidationError_1 = __importDefault(require("../../../models/errors/email/EmailValidationError"));
|
|
7
|
+
class EmailClient {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async sendTemplatedEmail(params) {
|
|
12
|
+
this.validateRecipientDomains(params.to);
|
|
13
|
+
await this.sendEmail(params);
|
|
14
|
+
}
|
|
15
|
+
validateRecipientDomains(to) {
|
|
16
|
+
const { stage, validateRecipientDomains } = this.config;
|
|
17
|
+
if (!validateRecipientDomains) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const isValidationDisabled = validateRecipientDomains.disableOnEnvironment &&
|
|
21
|
+
validateRecipientDomains.disableOnEnvironment.includes(stage);
|
|
22
|
+
if (isValidationDisabled) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const { allowedDomains } = validateRecipientDomains;
|
|
26
|
+
const hasInvalidRecipientDomains = allowedDomains && this.areRecipientDomainsInvalid(to, allowedDomains);
|
|
27
|
+
if (hasInvalidRecipientDomains) {
|
|
28
|
+
throw new EmailValidationError_1.default();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
areRecipientDomainsInvalid(addresses, allowedDomains) {
|
|
32
|
+
return addresses.some((address) => {
|
|
33
|
+
const [, domain] = address.split('@');
|
|
34
|
+
return !allowedDomains.includes(domain);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.default = EmailClient;
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../services/email/EmailClient/index.ts"],"names":[],"mappings":";;;;;AACA,6GAAoF;AAEpF,MAAe,WAAW;IAGxB,YAAyC,MAAe;QAAf,WAAM,GAAN,MAAM,CAAS;IAAG,CAAC;IASrD,KAAK,CAAC,kBAAkB,CAC7B,MAA0C;QAE1C,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAExC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAMO,wBAAwB,CAAC,EAAY;QAC3C,MAAM,EAAE,KAAK,EAAE,wBAAwB,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAEvD,IAAI,CAAC,wBAAwB,EAAE;YAC7B,OAAM;SACP;QAED,MAAM,oBAAoB,GACxB,wBAAwB,CAAC,oBAAoB;YAC7C,wBAAwB,CAAC,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAE/D,IAAI,oBAAoB,EAAE;YACxB,OAAM;SACP;QACD,MAAM,EAAE,cAAc,EAAE,GAAG,wBAAwB,CAAA;QAEnD,MAAM,0BAA0B,GAC9B,cAAc,IAAI,IAAI,CAAC,0BAA0B,CAAC,EAAE,EAAE,cAAc,CAAC,CAAA;QAEvE,IAAI,0BAA0B,EAAE;YAC9B,MAAM,IAAI,8BAAoB,EAAE,CAAA;SACjC;IACH,CAAC;IAEO,0BAA0B,CAChC,SAAmB,EACnB,cAAwB;QAExB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YAChC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAErC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAED,kBAAe,WAAW,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Environment from '../../../models/enum/Environment';
|
|
2
|
+
import Attachment from '../../../models/email/Attachment';
|
|
3
|
+
export interface RecipientValidationConfig {
|
|
4
|
+
allowedDomains: string[];
|
|
5
|
+
disableOnEnvironment?: Environment[];
|
|
6
|
+
}
|
|
7
|
+
export interface EmailClientConfig {
|
|
8
|
+
stage: Environment;
|
|
9
|
+
validateRecipientDomains?: RecipientValidationConfig;
|
|
10
|
+
}
|
|
11
|
+
export interface SendTemplatedEmailRequest<TValues> {
|
|
12
|
+
to: string[];
|
|
13
|
+
templateName: string;
|
|
14
|
+
templateValues: TValues;
|
|
15
|
+
attachments?: Attachment[];
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../../services/email/EmailClient/models.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Mail from 'nodemailer/lib/mailer';
|
|
2
|
+
import EmailClient from './EmailClient';
|
|
3
|
+
import { EmailClientConfig, SendTemplatedEmailRequest } from './EmailClient/models';
|
|
4
|
+
import SESTemplateClient from './SESTemplateClient';
|
|
5
|
+
export interface NodemailerConfig extends EmailClientConfig {
|
|
6
|
+
mailer: Mail;
|
|
7
|
+
templateClient: SESTemplateClient;
|
|
8
|
+
from: string;
|
|
9
|
+
requireBody?: boolean;
|
|
10
|
+
requireSubject?: boolean;
|
|
11
|
+
sourceArn?: string;
|
|
12
|
+
}
|
|
13
|
+
declare class NodemailerEmailClient extends EmailClient<NodemailerConfig> {
|
|
14
|
+
constructor(config: NodemailerConfig);
|
|
15
|
+
protected sendEmail<TValues>(params: SendTemplatedEmailRequest<TValues>): Promise<void>;
|
|
16
|
+
private prepareMailerOptions;
|
|
17
|
+
private validateEmailContent;
|
|
18
|
+
}
|
|
19
|
+
export default NodemailerEmailClient;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const EmailClient_1 = __importDefault(require("./EmailClient"));
|
|
7
|
+
const EmailValidationError_1 = __importDefault(require("../../models/errors/email/EmailValidationError"));
|
|
8
|
+
class NodemailerEmailClient extends EmailClient_1.default {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super(config);
|
|
11
|
+
}
|
|
12
|
+
async sendEmail(params) {
|
|
13
|
+
const { to, templateName, templateValues, attachments } = params;
|
|
14
|
+
const { mailer, templateClient } = this.config;
|
|
15
|
+
const email = await templateClient.createEmailFromTemplate(templateName, templateValues);
|
|
16
|
+
this.validateEmailContent(email);
|
|
17
|
+
const mailOptions = this.prepareMailerOptions(to, email, attachments);
|
|
18
|
+
await mailer.sendMail(mailOptions);
|
|
19
|
+
}
|
|
20
|
+
prepareMailerOptions(to, email, attachments) {
|
|
21
|
+
const mailerAttachments = attachments &&
|
|
22
|
+
attachments.map((attachment) => ({
|
|
23
|
+
filename: attachment.fileName,
|
|
24
|
+
contentType: attachment.mimeType,
|
|
25
|
+
content: attachment.data,
|
|
26
|
+
}));
|
|
27
|
+
const { from } = this.config;
|
|
28
|
+
const mailOptions = {
|
|
29
|
+
from,
|
|
30
|
+
to,
|
|
31
|
+
attachments: mailerAttachments,
|
|
32
|
+
...email,
|
|
33
|
+
};
|
|
34
|
+
const { sourceArn } = this.config;
|
|
35
|
+
if (sourceArn) {
|
|
36
|
+
mailOptions.ses = {
|
|
37
|
+
SourceArn: sourceArn,
|
|
38
|
+
FromArn: sourceArn,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return mailOptions;
|
|
42
|
+
}
|
|
43
|
+
validateEmailContent(content) {
|
|
44
|
+
const { html, text, subject } = content;
|
|
45
|
+
const { requireSubject, requireBody } = this.config;
|
|
46
|
+
const hasEmailBody = html || text;
|
|
47
|
+
const noEmailBodyErrorCondition = !hasEmailBody && requireBody;
|
|
48
|
+
const noSubjectErrorCondition = !subject && requireSubject;
|
|
49
|
+
if (noEmailBodyErrorCondition || noSubjectErrorCondition) {
|
|
50
|
+
throw new EmailValidationError_1.default();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.default = NodemailerEmailClient;
|
|
55
|
+
//# sourceMappingURL=NodemailerEmailClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodemailerEmailClient.js","sourceRoot":"","sources":["../../../services/email/NodemailerEmailClient.ts"],"names":[],"mappings":";;;;;AAGA,gEAAuC;AAOvC,0GAAiF;AAWjF,MAAM,qBAAsB,SAAQ,qBAA6B;IAC/D,YAAmB,MAAwB;QACzC,KAAK,CAAC,MAAM,CAAC,CAAA;IACf,CAAC;IAUS,KAAK,CAAC,SAAS,CACvB,MAA0C;QAE1C,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,GAAG,MAAM,CAAA;QAChE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAE9C,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,uBAAuB,CACxD,YAAY,EACZ,cAAc,CACf,CAAA;QACD,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;QAEhC,MAAM,WAAW,GAAG,IAAI,CAAC,oBAAoB,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,CAAA;QAErE,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IACpC,CAAC;IAEO,oBAAoB,CAC1B,EAAY,EACZ,KAAmB,EACnB,WAA0B;QAE1B,MAAM,iBAAiB,GACrB,WAAW;YACX,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC/B,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,WAAW,EAAE,UAAU,CAAC,QAAQ;gBAChC,OAAO,EAAE,UAAU,CAAC,IAAI;aACzB,CAAC,CAAC,CAAA;QAEL,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAC5B,MAAM,WAAW,GAAgB;YAC/B,IAAI;YACJ,EAAE;YACF,WAAW,EAAE,iBAAiB;YAC9B,GAAG,KAAK;SACT,CAAA;QAED,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAEjC,IAAI,SAAS,EAAE;YACb,WAAW,CAAC,GAAG,GAAG;gBAChB,SAAS,EAAE,SAAS;gBACpB,OAAO,EAAE,SAAS;aACnB,CAAA;SACF;QAED,OAAO,WAAW,CAAA;IACpB,CAAC;IAEO,oBAAoB,CAAC,OAAqB;QAChD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;QACvC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QAEnD,MAAM,YAAY,GAAG,IAAI,IAAI,IAAI,CAAA;QACjC,MAAM,yBAAyB,GAAG,CAAC,YAAY,IAAI,WAAW,CAAA;QAC9D,MAAM,uBAAuB,GAAG,CAAC,OAAO,IAAI,cAAc,CAAA;QAE1D,IAAI,yBAAyB,IAAI,uBAAuB,EAAE;YACxD,MAAM,IAAI,8BAAoB,EAAE,CAAA;SACjC;IACH,CAAC;CACF;AAED,kBAAe,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Template from '../../models/email/Template';
|
|
2
|
+
import EmailContent from '../../models/email/EmailContent';
|
|
3
|
+
import TemplateClient from './TemplateClient';
|
|
4
|
+
import { SES } from 'aws-sdk';
|
|
5
|
+
import { Cache } from '../../utils/caching';
|
|
6
|
+
declare class SESTemplateClient extends TemplateClient {
|
|
7
|
+
private readonly sesClient;
|
|
8
|
+
private readonly cache;
|
|
9
|
+
constructor(sesClient: SES, cache: Cache);
|
|
10
|
+
createEmailFromTemplate<TValues>(templateName: string, templateValues: TValues): Promise<EmailContent>;
|
|
11
|
+
getTemplate(templateName: string): Promise<Template>;
|
|
12
|
+
private assignTemplateValues;
|
|
13
|
+
private moveSesTemplateToCache;
|
|
14
|
+
private static sesTemplateToTemplate;
|
|
15
|
+
}
|
|
16
|
+
export default SESTemplateClient;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const TemplateClient_1 = __importDefault(require("./TemplateClient"));
|
|
7
|
+
const TemplateNotFoundError_1 = __importDefault(require("../../models/errors/email/TemplateNotFoundError"));
|
|
8
|
+
class SESTemplateClient extends TemplateClient_1.default {
|
|
9
|
+
constructor(sesClient, cache) {
|
|
10
|
+
super();
|
|
11
|
+
this.sesClient = sesClient;
|
|
12
|
+
this.cache = cache;
|
|
13
|
+
}
|
|
14
|
+
async createEmailFromTemplate(templateName, templateValues) {
|
|
15
|
+
const template = await this.getTemplate(templateName);
|
|
16
|
+
return this.assignTemplateValues(template, templateValues);
|
|
17
|
+
}
|
|
18
|
+
async getTemplate(templateName) {
|
|
19
|
+
const template = await this.cache.get(templateName);
|
|
20
|
+
return template || this.moveSesTemplateToCache(templateName);
|
|
21
|
+
}
|
|
22
|
+
assignTemplateValues(template, values) {
|
|
23
|
+
const templateParts = {
|
|
24
|
+
html: template.html,
|
|
25
|
+
text: template.text,
|
|
26
|
+
subject: template.subject,
|
|
27
|
+
};
|
|
28
|
+
return Object.keys(templateParts).reduce((email, emailPartKey) => {
|
|
29
|
+
const templateKey = emailPartKey;
|
|
30
|
+
if (!template[templateKey]) {
|
|
31
|
+
return email;
|
|
32
|
+
}
|
|
33
|
+
const templateData = SESTemplateClient.replaceTemplateValues(template[templateKey], values);
|
|
34
|
+
return {
|
|
35
|
+
...email,
|
|
36
|
+
[emailPartKey]: templateData,
|
|
37
|
+
};
|
|
38
|
+
}, {});
|
|
39
|
+
}
|
|
40
|
+
async moveSesTemplateToCache(templateName) {
|
|
41
|
+
const { Template: sesTemplate } = await this.sesClient
|
|
42
|
+
.getTemplate({ TemplateName: templateName })
|
|
43
|
+
.promise();
|
|
44
|
+
if (!sesTemplate) {
|
|
45
|
+
throw new TemplateNotFoundError_1.default(templateName);
|
|
46
|
+
}
|
|
47
|
+
const template = SESTemplateClient.sesTemplateToTemplate(sesTemplate);
|
|
48
|
+
const { name } = template;
|
|
49
|
+
await this.cache.add(name, template);
|
|
50
|
+
return template;
|
|
51
|
+
}
|
|
52
|
+
static sesTemplateToTemplate(sesTemplate) {
|
|
53
|
+
return {
|
|
54
|
+
name: sesTemplate.TemplateName,
|
|
55
|
+
subject: sesTemplate.SubjectPart,
|
|
56
|
+
text: sesTemplate.TextPart,
|
|
57
|
+
html: sesTemplate.HtmlPart,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.default = SESTemplateClient;
|
|
62
|
+
//# sourceMappingURL=SESTemplateClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SESTemplateClient.js","sourceRoot":"","sources":["../../../services/email/SESTemplateClient.ts"],"names":[],"mappings":";;;;;AAGA,sEAA6C;AAG7C,4GAAmF;AAEnF,MAAM,iBAAkB,SAAQ,wBAAc;IAC5C,YAA6B,SAAc,EAAmB,KAAY;QACxE,KAAK,EAAE,CAAA;QADoB,cAAS,GAAT,SAAS,CAAK;QAAmB,UAAK,GAAL,KAAK,CAAO;IAE1E,CAAC;IAQM,KAAK,CAAC,uBAAuB,CAClC,YAAoB,EACpB,cAAuB;QAEvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;QAErD,OAAO,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;IAC5D,CAAC;IASM,KAAK,CAAC,WAAW,CAAC,YAAoB;QAC3C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAW,YAAY,CAAC,CAAA;QAE7D,OAAO,QAAQ,IAAI,IAAI,CAAC,sBAAsB,CAAC,YAAY,CAAC,CAAA;IAC9D,CAAC;IAEO,oBAAoB,CAC1B,QAAkB,EAClB,MAAe;QAEf,MAAM,aAAa,GAAG;YACpB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,OAAO,EAAE,QAAQ,CAAC,OAAO;SAC1B,CAAA;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,CACtC,CAAC,KAAmB,EAAE,YAAoB,EAAE,EAAE;YAC5C,MAAM,WAAW,GAAG,YAA8B,CAAA;YAElD,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE;gBAC1B,OAAO,KAAK,CAAA;aACb;YAED,MAAM,YAAY,GAAG,iBAAiB,CAAC,qBAAqB,CAC1D,QAAQ,CAAC,WAAW,CAAW,EAC/B,MAAM,CACP,CAAA;YAED,OAAO;gBACL,GAAG,KAAK;gBACR,CAAC,YAAY,CAAC,EAAE,YAAY;aAC7B,CAAA;QACH,CAAC,EACD,EAAkB,CACnB,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAClC,YAAoB;QAEpB,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS;aACnD,WAAW,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;aAC3C,OAAO,EAAE,CAAA;QAEZ,IAAI,CAAC,WAAW,EAAE;YAChB,MAAM,IAAI,+BAAqB,CAAC,YAAY,CAAC,CAAA;SAC9C;QAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAA;QAErE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAA;QACzB,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAW,IAAI,EAAE,QAAQ,CAAC,CAAA;QAE9C,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,MAAM,CAAC,qBAAqB,CAAC,WAAyB;QAC5D,OAAO;YACL,IAAI,EAAE,WAAW,CAAC,YAAY;YAC9B,OAAO,EAAE,WAAW,CAAC,WAAW;YAChC,IAAI,EAAE,WAAW,CAAC,QAAQ;YAC1B,IAAI,EAAE,WAAW,CAAC,QAAQ;SAC3B,CAAA;IACH,CAAC;CACF;AAED,kBAAe,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import EmailContent from '../../models/email/EmailContent';
|
|
2
|
+
declare abstract class TemplateClient {
|
|
3
|
+
abstract createEmailFromTemplate<TValues>(templateName: string, templateValues: TValues): Promise<EmailContent>;
|
|
4
|
+
protected static replaceTemplateValues<TValues = unknown>(template: string, values: TValues): string;
|
|
5
|
+
}
|
|
6
|
+
export default TemplateClient;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class TemplateClient {
|
|
4
|
+
static replaceTemplateValues(template, values) {
|
|
5
|
+
const expression = /\{\{([a-zA-Z0-9\\-]+)\}\}/g;
|
|
6
|
+
return template.replace(expression, (match, key) => {
|
|
7
|
+
return String(values[key] || match);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.default = TemplateClient;
|
|
12
|
+
//# sourceMappingURL=TemplateClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TemplateClient.js","sourceRoot":"","sources":["../../../services/email/TemplateClient.ts"],"names":[],"mappings":";;AAEA,MAAe,cAAc;IAejB,MAAM,CAAC,qBAAqB,CACpC,QAAgB,EAChB,MAAe;QAEf,MAAM,UAAU,GAAG,4BAA4B,CAAA;QAC/C,OAAO,QAAQ,CAAC,OAAO,CACrB,UAAU,EACV,CAA6B,KAAa,EAAE,GAAS,EAAE,EAAE;YACvD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAA;QACrC,CAAC,CACF,CAAA;IACH,CAAC;CACF;AAED,kBAAe,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const faker_1 = require("faker");
|
|
7
|
+
const NodemailerEmailClient_1 = __importDefault(require("../NodemailerEmailClient"));
|
|
8
|
+
const Environment_1 = __importDefault(require("../../../models/enum/Environment"));
|
|
9
|
+
const EmailValidationError_1 = __importDefault(require("../../../models/errors/email/EmailValidationError"));
|
|
10
|
+
const createMockMailer = () => ({
|
|
11
|
+
sendMail: jest.fn(),
|
|
12
|
+
});
|
|
13
|
+
const emailContent = {
|
|
14
|
+
subject: 'A subject',
|
|
15
|
+
html: `<html><body>An email from ${faker_1.name.firstName()} </body></html>`,
|
|
16
|
+
};
|
|
17
|
+
const createMockSESTemplateClient = () => ({
|
|
18
|
+
createEmailFromTemplate: jest.fn().mockResolvedValue(emailContent),
|
|
19
|
+
});
|
|
20
|
+
describe('NodemailerEmailClient', () => {
|
|
21
|
+
let mockMailer;
|
|
22
|
+
let mockSESTemplateClient;
|
|
23
|
+
let sut;
|
|
24
|
+
let config;
|
|
25
|
+
const createConfig = (from, stage, validateRecipientDomains) => {
|
|
26
|
+
mockMailer = createMockMailer();
|
|
27
|
+
mockSESTemplateClient = createMockSESTemplateClient();
|
|
28
|
+
return {
|
|
29
|
+
mailer: mockMailer,
|
|
30
|
+
templateClient: mockSESTemplateClient,
|
|
31
|
+
from,
|
|
32
|
+
stage,
|
|
33
|
+
validateRecipientDomains,
|
|
34
|
+
requireBody: true,
|
|
35
|
+
requireSubject: true,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
describe('sendTemplatedEmail', () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
config = createConfig('foo@bar.baz', Environment_1.default.Dev);
|
|
41
|
+
sut = new NodemailerEmailClient_1.default(config);
|
|
42
|
+
});
|
|
43
|
+
it('Should send an email if a recipient validation rule is set but the environment is exempted', async () => {
|
|
44
|
+
config = createConfig('foo@bar.baz', Environment_1.default.Prod, {
|
|
45
|
+
allowedDomains: ['bar.baz'],
|
|
46
|
+
disableOnEnvironment: [Environment_1.default.Prod],
|
|
47
|
+
});
|
|
48
|
+
sut = new NodemailerEmailClient_1.default(config);
|
|
49
|
+
await sut.sendTemplatedEmail({
|
|
50
|
+
to: ['bar@bar.baz'],
|
|
51
|
+
templateName: 'a template',
|
|
52
|
+
templateValues: { name: 'sender' },
|
|
53
|
+
});
|
|
54
|
+
expect(mockMailer.sendMail).toBeCalledTimes(1);
|
|
55
|
+
});
|
|
56
|
+
it('Should throw an error if the email created from the template does not contain neither a subject nor body', async () => {
|
|
57
|
+
const params = {
|
|
58
|
+
to: ['bar@bar.baz'],
|
|
59
|
+
templateName: 'a template',
|
|
60
|
+
templateValues: { name: 'sender' },
|
|
61
|
+
};
|
|
62
|
+
mockSESTemplateClient.createEmailFromTemplate.mockResolvedValueOnce({});
|
|
63
|
+
await expect(async () => sut.sendTemplatedEmail(params)).rejects.toThrow(EmailValidationError_1.default);
|
|
64
|
+
mockSESTemplateClient.createEmailFromTemplate.mockResolvedValueOnce({
|
|
65
|
+
text: 'A body',
|
|
66
|
+
});
|
|
67
|
+
await expect(async () => sut.sendTemplatedEmail(params)).rejects.toThrow(EmailValidationError_1.default);
|
|
68
|
+
});
|
|
69
|
+
it('Should throw an error if attempting to send an email from a non-whitelisted domain.', async () => {
|
|
70
|
+
config = createConfig('foo@bar.baz', Environment_1.default.Dev, {
|
|
71
|
+
allowedDomains: ['bar.baz'],
|
|
72
|
+
});
|
|
73
|
+
sut = new NodemailerEmailClient_1.default(config);
|
|
74
|
+
const params = {
|
|
75
|
+
to: ['bar@forbidden.email'],
|
|
76
|
+
templateName: 'a template',
|
|
77
|
+
templateValues: { name: 'sender' },
|
|
78
|
+
};
|
|
79
|
+
await expect(async () => sut.sendTemplatedEmail(params)).rejects.toThrow(EmailValidationError_1.default);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=NodemailerEmailClient.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodemailerEmailClient.spec.js","sourceRoot":"","sources":["../../../../services/email/__tests__/NodemailerEmailClient.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const faker_1 = require("faker");
|
|
4
|
+
const __1 = require("..");
|
|
5
|
+
const createMockCache = () => ({
|
|
6
|
+
get: jest.fn(),
|
|
7
|
+
add: jest.fn(),
|
|
8
|
+
});
|
|
9
|
+
const createMockSES = () => ({
|
|
10
|
+
getTemplate: jest.fn(),
|
|
11
|
+
});
|
|
12
|
+
describe('SESTemplateClient', () => {
|
|
13
|
+
let mockCache;
|
|
14
|
+
let mockSES;
|
|
15
|
+
let sut;
|
|
16
|
+
describe('createEmailFromTemplate', () => {
|
|
17
|
+
mockCache = createMockCache();
|
|
18
|
+
const templateName = 'The template name';
|
|
19
|
+
const sesTemplate = {
|
|
20
|
+
Template: {
|
|
21
|
+
TemplateName: templateName,
|
|
22
|
+
SubjectPart: 'An email subject from {{name}}',
|
|
23
|
+
HtmlPart: '<html><body> An email body from {{name}} of {{company}} </body></html>',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const templateValues = {
|
|
27
|
+
name: faker_1.name.firstName(),
|
|
28
|
+
company: faker_1.company.companyName(),
|
|
29
|
+
};
|
|
30
|
+
const expected = {
|
|
31
|
+
subject: `An email subject from ${templateValues.name}`,
|
|
32
|
+
html: `<html><body> An email body from ${templateValues.name} of ${templateValues.company} </body></html>`,
|
|
33
|
+
};
|
|
34
|
+
it('Should create valid email content from an SES template', async () => {
|
|
35
|
+
mockSES = createMockSES();
|
|
36
|
+
mockSES.getTemplate.mockImplementation(() => ({
|
|
37
|
+
promise: jest.fn().mockResolvedValue(sesTemplate),
|
|
38
|
+
}));
|
|
39
|
+
sut = new __1.SESTemplateClient(mockSES, mockCache);
|
|
40
|
+
const email = await sut.createEmailFromTemplate(templateName, templateValues);
|
|
41
|
+
expect(email).toEqual(expected);
|
|
42
|
+
});
|
|
43
|
+
it('Should create valid email content from a template retrieved from the cache', async () => {
|
|
44
|
+
const template = {
|
|
45
|
+
name: templateName,
|
|
46
|
+
subject: 'An email subject from {{name}}',
|
|
47
|
+
html: '<html><body> An email body from {{name}} of {{company}} </body></html>',
|
|
48
|
+
};
|
|
49
|
+
mockCache.get.mockResolvedValueOnce(template);
|
|
50
|
+
const email = await sut.createEmailFromTemplate(templateName, templateValues);
|
|
51
|
+
expect(email).toEqual(expected);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=SESTemplateClient.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SESTemplateClient.spec.js","sourceRoot":"","sources":["../../../../services/email/__tests__/SESTemplateClient.spec.ts"],"names":[],"mappings":";;AAAA,iCAAqC;AAErC,0BAAsC;AAGtC,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,CAAC;IAC7B,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;IACd,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;CACf,CAAC,CAAA;AAEF,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,CAAC;IAC3B,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE;CACvB,CAAC,CAAA;AAEF,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,SAA6C,CAAA;IACjD,IAAI,OAAyC,CAAA;IAC7C,IAAI,GAAsB,CAAA;IAE1B,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,SAAS,GAAG,eAAe,EAAE,CAAA;QAC7B,MAAM,YAAY,GAAG,mBAAmB,CAAA;QACxC,MAAM,WAAW,GAAG;YAClB,QAAQ,EAAE;gBACR,YAAY,EAAE,YAAY;gBAC1B,WAAW,EAAE,gCAAgC;gBAC7C,QAAQ,EACN,wEAAwE;aAC3E;SACF,CAAA;QAED,MAAM,cAAc,GAAG;YACrB,IAAI,EAAE,YAAI,CAAC,SAAS,EAAE;YACtB,OAAO,EAAE,eAAO,CAAC,WAAW,EAAE;SAC/B,CAAA;QAED,MAAM,QAAQ,GAAG;YACf,OAAO,EAAE,yBAAyB,cAAc,CAAC,IAAI,EAAE;YACvD,IAAI,EAAE,mCAAmC,cAAc,CAAC,IAAI,OAAO,cAAc,CAAC,OAAO,iBAAiB;SAC3G,CAAA;QAED,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;YACtE,OAAO,GAAG,aAAa,EAAE,CAAA;YACzB,OAAO,CAAC,WAAW,CAAC,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC5C,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC;aAClD,CAAC,CAAC,CAAA;YAEH,GAAG,GAAG,IAAI,qBAAiB,CACzB,OAAyB,EACzB,SAA6B,CAC9B,CAAA;YAED,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,uBAAuB,CAC7C,YAAY,EACZ,cAAc,CACf,CAAA;YAED,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACjC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;YAC1F,MAAM,QAAQ,GAAG;gBACf,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,gCAAgC;gBACzC,IAAI,EAAE,wEAAwE;aAC/E,CAAA;YAED,SAAS,CAAC,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAA;YAE7C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,uBAAuB,CAC7C,YAAY,EACZ,cAAc,CACf,CAAA;YAED,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACjC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TemplateClient = exports.SESTemplateClient = exports.NodemailerEmailClient = exports.EmailClient = void 0;
|
|
7
|
+
var EmailClient_1 = require("./EmailClient");
|
|
8
|
+
Object.defineProperty(exports, "EmailClient", { enumerable: true, get: function () { return __importDefault(EmailClient_1).default; } });
|
|
9
|
+
var NodemailerEmailClient_1 = require("./NodemailerEmailClient");
|
|
10
|
+
Object.defineProperty(exports, "NodemailerEmailClient", { enumerable: true, get: function () { return __importDefault(NodemailerEmailClient_1).default; } });
|
|
11
|
+
var SESTemplateClient_1 = require("./SESTemplateClient");
|
|
12
|
+
Object.defineProperty(exports, "SESTemplateClient", { enumerable: true, get: function () { return __importDefault(SESTemplateClient_1).default; } });
|
|
13
|
+
var TemplateClient_1 = require("./TemplateClient");
|
|
14
|
+
Object.defineProperty(exports, "TemplateClient", { enumerable: true, get: function () { return __importDefault(TemplateClient_1).default; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../services/email/index.ts"],"names":[],"mappings":";;;;;;AAAA,6CAAsD;AAA7C,2HAAA,OAAO,OAAe;AAC/B,iEAA0E;AAAjE,+IAAA,OAAO,OAAyB;AACzC,yDAAkE;AAAzD,uIAAA,OAAO,OAAqB;AACrC,mDAA4D;AAAnD,iIAAA,OAAO,OAAkB"}
|
|
@@ -34,6 +34,10 @@ class GlobalAtomicCache extends Cache_1.default {
|
|
|
34
34
|
this.fetchers = new Map();
|
|
35
35
|
}
|
|
36
36
|
getLockFilePath(key) {
|
|
37
|
+
const lockfilePath = path.join(this.mountPath, `portal-${this.storeName}-${key}.lck`);
|
|
38
|
+
this.logger.info('[GlobalAtomicCache] - Getting lockfile path', {
|
|
39
|
+
lockfilePath,
|
|
40
|
+
});
|
|
37
41
|
return path.join(this.mountPath, `portal-${this.storeName}-${key}.lck`);
|
|
38
42
|
}
|
|
39
43
|
async isLockFilePresent(key) {
|
|
@@ -69,10 +73,12 @@ class GlobalAtomicCache extends Cache_1.default {
|
|
|
69
73
|
async waitOrDeleteLock(key) {
|
|
70
74
|
const watcher = (0, fs_1.watch)(this.getLockFilePath(key));
|
|
71
75
|
const watchPromise = new Promise((resolve) => {
|
|
76
|
+
this.logger.info('[GlobalAtomicCache] - Deleting lockfile', { key });
|
|
72
77
|
watcher.on('change', async () => {
|
|
73
78
|
const isLockFilePresent = await this.isLockFilePresent(key);
|
|
74
79
|
if (!isLockFilePresent) {
|
|
75
80
|
resolve(true);
|
|
81
|
+
this.logger.info('[GlobalAtomicCache] - Lockfile deleted', { key });
|
|
76
82
|
}
|
|
77
83
|
});
|
|
78
84
|
});
|
|
@@ -85,6 +91,9 @@ class GlobalAtomicCache extends Cache_1.default {
|
|
|
85
91
|
}
|
|
86
92
|
async executeFactoryAtomically(key, factory) {
|
|
87
93
|
const isLockFilePresent = await this.isLockFilePresent(key);
|
|
94
|
+
this.logger.info('[GlobalAtomicCache] - Checking file existence', {
|
|
95
|
+
isLockFilePresent,
|
|
96
|
+
});
|
|
88
97
|
if (isLockFilePresent) {
|
|
89
98
|
await this.waitOrDeleteLock(key);
|
|
90
99
|
return this.getOrAdd(key, factory);
|
|
@@ -105,6 +114,10 @@ class GlobalAtomicCache extends Cache_1.default {
|
|
|
105
114
|
}
|
|
106
115
|
async getOrAdd(key, factory) {
|
|
107
116
|
const fetcher = this.fetchers.get(key);
|
|
117
|
+
this.logger.info('[GlobalAtomicCache] - Getting or adding cached value', {
|
|
118
|
+
fetchers: this.fetchers,
|
|
119
|
+
key,
|
|
120
|
+
});
|
|
108
121
|
if (fetcher) {
|
|
109
122
|
return fetcher;
|
|
110
123
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GlobalAtomicCache.js","sourceRoot":"","sources":["../../../utils/caching/GlobalAtomicCache.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,2BAA0C;AAC1C,2CAA4B;AAC5B,oDAA2B;AAU3B,MAAe,iBAAkB,SAAQ,eAAK;IAG5C,YACE,gBAAwB,EACL,SAAiB,EACjB,SAAiB,EACjB,MAAc;QAEjC,KAAK,CAAC,gBAAgB,CAAC,CAAA;QAJJ,cAAS,GAAT,SAAS,CAAQ;QACjB,cAAS,GAAT,SAAS,CAAQ;QACjB,WAAM,GAAN,MAAM,CAAQ;QANlB,aAAQ,GAAG,IAAI,GAAG,EAA4B,CAAA;IAS/D,CAAC;IAEO,eAAe,CAAC,GAAW;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,IAAI,CAAC,SAAS,IAAI,GAAG,MAAM,CAAC,CAAA;IACzE,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,GAAW;QACzC,IAAI;YACF,MAAM,aAAE,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAA;YAE1C,OAAO,IAAI,CAAA;SACZ;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,KAAK,CAAA;SACb;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,GAAW;QACtC,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAE3D,IAAI,iBAAiB,EAAE;YACrB,IAAI;gBACF,MAAM,aAAE,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAA;aAC3C;YAAC,OAAO,KAAK,EAAE;gBACd,MAAM,OAAO,GAAG,KAAgB,CAAA;gBAEhC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE;oBAC7B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,sEAAsE,CACvE,CAAA;iBACF;qBAAM;oBACL,MAAM,KAAK,CAAA;iBACZ;aACF;SACF;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,GAAW;QACnC,MAAM,IAAI,GAAG,MAAM,aAAE,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAA;QAC3D,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,GAAW;QACxC,MAAM,OAAO,GAAG,IAAA,UAAK,EAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAA;QAEhD,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;gBAC9B,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;gBAE3D,IAAI,CAAC,iBAAiB,EAAE;oBACtB,OAAO,CAAC,IAAI,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"GlobalAtomicCache.js","sourceRoot":"","sources":["../../../utils/caching/GlobalAtomicCache.ts"],"names":[],"mappings":"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"}
|