@futdevpro/nts-dynamo 1.9.14 → 1.9.16

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.
Files changed (142) hide show
  1. package/.copilot/patterns.json +7 -7
  2. package/.eslintrc.json +1 -1
  3. package/.github/workflows/main.yml +206 -0
  4. package/HOWTO.md +15 -15
  5. package/README.md +140 -140
  6. package/build/_constants/mocks/auth-service.mock.d.ts +1 -1
  7. package/build/_constants/mocks/auth-service.mock.d.ts.map +1 -1
  8. package/build/_constants/mocks/auth-service.mock.js +1 -1
  9. package/build/_constants/mocks/auth-service.mock.js.map +1 -1
  10. package/build/_models/control-models/endpoint-params.control-model.d.ts.map +1 -1
  11. package/build/_models/control-models/endpoint-params.control-model.js +2 -0
  12. package/build/_models/control-models/endpoint-params.control-model.js.map +1 -1
  13. package/build/_models/control-models/socket-event.control-model.js +1 -1
  14. package/build/_modules/usage/usage.controller.js +1 -1
  15. package/build/_modules/usage/usage.controller.js.map +1 -1
  16. package/build/_services/base/db.service.js +3 -3
  17. package/build/_services/base/db.service.js.map +1 -1
  18. package/build/_services/core/auth.service.d.ts +3 -3
  19. package/build/_services/core/auth.service.d.ts.map +1 -1
  20. package/build/_services/core/global.service.d.ts.map +1 -1
  21. package/build/_services/core/global.service.js +1 -0
  22. package/build/_services/core/global.service.js.map +1 -1
  23. package/build/_services/route/controller.service.d.ts +3 -3
  24. package/build/_services/route/controller.service.js +2 -2
  25. package/build/_services/route/routing-module.service.d.ts +1 -0
  26. package/build/_services/route/routing-module.service.d.ts.map +1 -1
  27. package/build/_services/route/routing-module.service.js +17 -23
  28. package/build/_services/route/routing-module.service.js.map +1 -1
  29. package/build/_services/server/app.server.d.ts.map +1 -1
  30. package/build/_services/server/app.server.js +4 -1
  31. package/build/_services/server/app.server.js.map +1 -1
  32. package/build/_services/socket/socket-client.service.d.ts.map +1 -1
  33. package/build/_services/socket/socket-client.service.js +1 -0
  34. package/build/_services/socket/socket-client.service.js.map +1 -1
  35. package/nodemon.json +17 -15
  36. package/package.json +6 -6
  37. package/src/_constants/global-settings.const.ts +27 -27
  38. package/src/_constants/index.ts +2 -2
  39. package/src/_constants/mocks/app-extended-server.mock.ts +198 -198
  40. package/src/_constants/mocks/app-params.mock.ts +9 -9
  41. package/src/_constants/mocks/app-server.mock.ts +185 -185
  42. package/src/_constants/mocks/auth-service.mock.ts +28 -28
  43. package/src/_constants/mocks/controller.mock.ts +16 -16
  44. package/src/_constants/mocks/data-model.mock.ts +83 -83
  45. package/src/_constants/mocks/email-service-collection.mock.ts +13 -13
  46. package/src/_constants/mocks/email-service.mock.ts +19 -19
  47. package/src/_constants/mocks/email-template.mock.html +14 -14
  48. package/src/_constants/mocks/endpoint.mock.ts +90 -90
  49. package/src/_constants/mocks/socket-client.mock.ts +43 -43
  50. package/src/_constants/mocks/socket-server.mock.ts +43 -43
  51. package/src/_enums/data-model-type.enum.ts +14 -14
  52. package/src/_enums/data-service-function.enum.ts +15 -15
  53. package/src/_enums/http/http-call-type.enum.ts +12 -12
  54. package/src/_enums/http/http-response-type.enum.ts +7 -7
  55. package/src/_enums/http/socket-event-type.enum.ts +18 -18
  56. package/src/_enums/index.ts +13 -13
  57. package/src/_enums/predefined-data-types.enum.ts +27 -27
  58. package/src/_enums/route-security.enum.ts +12 -12
  59. package/src/_enums/socket-security.enum.ts +11 -11
  60. package/src/_models/control-models/api-call-params.control-model.ts +126 -126
  61. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  62. package/src/_models/control-models/app-params.control-model.ts +45 -45
  63. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  64. package/src/_models/control-models/endpoint-params.control-model.ts +309 -307
  65. package/src/_models/control-models/http-settings.control-model.ts +29 -29
  66. package/src/_models/control-models/index.ts +13 -13
  67. package/src/_models/control-models/socket-client-service-params.control-model.ts +28 -28
  68. package/src/_models/control-models/socket-event.control-model.ts +150 -150
  69. package/src/_models/control-models/socket-presence.control-model.ts +207 -207
  70. package/src/_models/control-models/socket-server-service-params.control-model.ts +20 -20
  71. package/src/_models/control-models/system-control.control-model.ts +12 -12
  72. package/src/_models/index.ts +9 -9
  73. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  74. package/src/_models/interfaces/global-service-settings.interface.ts +45 -45
  75. package/src/_models/interfaces/global-settings.interface.ts +83 -83
  76. package/src/_models/interfaces/index.ts +7 -7
  77. package/src/_models/interfaces/routing-module-settings.interface.ts +20 -20
  78. package/src/_models/types/db-filter.type.ts +108 -108
  79. package/src/_models/types/db-update.type.ts +100 -100
  80. package/src/_models/types/index.ts +5 -5
  81. package/src/_modules/api-service.index.ts +12 -12
  82. package/src/_modules/app-extended.index.ts +28 -28
  83. package/src/_modules/app.index.ts +24 -24
  84. package/src/_modules/auth.index.ts +7 -7
  85. package/src/_modules/constants.index.ts +2 -2
  86. package/src/_modules/controller.index.ts +10 -10
  87. package/src/_modules/custom-data/custom-data.controller.ts +69 -69
  88. package/src/_modules/custom-data/custom-data.data-service.ts +20 -20
  89. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +23 -23
  90. package/src/_modules/custom-data/index.ts +6 -6
  91. package/src/_modules/custom-data-module.index.ts +2 -2
  92. package/src/_modules/data-service.index.ts +9 -9
  93. package/src/_modules/email.index.ts +8 -8
  94. package/src/_modules/enums.index.ts +2 -2
  95. package/src/_modules/extended.index.ts +8 -8
  96. package/src/_modules/models.index.ts +2 -2
  97. package/src/_modules/services.index.ts +2 -2
  98. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  99. package/src/_modules/test/index.ts +5 -5
  100. package/src/_modules/test/test.controller.ts +115 -115
  101. package/src/_modules/test-module.index.ts +2 -2
  102. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  103. package/src/_modules/usage/index.ts +7 -7
  104. package/src/_modules/usage/usage.controller.ts +120 -120
  105. package/src/_modules/usage/usage.data-service.ts +172 -172
  106. package/src/_modules/usage-module.index.ts +2 -2
  107. package/src/_services/base/data.service.ts +921 -921
  108. package/src/_services/base/db.service.spec.ts +32 -32
  109. package/src/_services/base/db.service.ts +1063 -1063
  110. package/src/_services/base/singleton.service.ts +21 -21
  111. package/src/_services/core/api.service.ts +453 -453
  112. package/src/_services/core/auth.service.ts +172 -172
  113. package/src/_services/core/email.service.ts +678 -678
  114. package/src/_services/core/global.service.ts +270 -269
  115. package/src/_services/core/service-collection.service.ts +5 -5
  116. package/src/_services/index.ts +23 -23
  117. package/src/_services/route/controller.service.ts +129 -129
  118. package/src/_services/route/routing-module.service.ts +293 -273
  119. package/src/_services/server/app-extended.server.spec.ts +76 -76
  120. package/src/_services/server/app-extended.server.ts +520 -520
  121. package/src/_services/server/app.server.spec.ts +67 -67
  122. package/src/_services/server/app.server.ts +1181 -1179
  123. package/src/_services/shared.service.spec.ts +19 -19
  124. package/src/_services/shared.static-service.ts +73 -73
  125. package/src/_services/socket/socket-client.service.ts +236 -235
  126. package/src/_services/socket/socket-server.service.spec.ts +11 -11
  127. package/src/_services/socket/socket-server.service.ts +761 -761
  128. package/src/index.ts +18 -18
  129. package/tsconfig.json +41 -46
  130. package/build/tsconfig.tsbuildinfo +0 -1
  131. package/src/_enums/http/http-call-type.enum.d.ts +0 -12
  132. package/src/_enums/http/http-call-type.enum.d.ts.map +0 -1
  133. package/src/_enums/http/http-call-type.enum.js +0 -16
  134. package/src/_enums/http/http-call-type.enum.js.map +0 -1
  135. package/src/_enums/http/http-response-type.enum.d.ts +0 -7
  136. package/src/_enums/http/http-response-type.enum.d.ts.map +0 -1
  137. package/src/_enums/http/http-response-type.enum.js +0 -11
  138. package/src/_enums/http/http-response-type.enum.js.map +0 -1
  139. package/src/_enums/http/socket-event-type.enum.d.ts +0 -15
  140. package/src/_enums/http/socket-event-type.enum.d.ts.map +0 -1
  141. package/src/_enums/http/socket-event-type.enum.js +0 -19
  142. package/src/_enums/http/socket-event-type.enum.js.map +0 -1
@@ -1,678 +1,678 @@
1
-
2
- import * as FileSystem from 'fs';
3
- import * as Path from 'path';
4
- import * as NodeMailer from 'nodemailer';
5
-
6
- import { Options as MailOptions, Attachment } from 'nodemailer/lib/mailer';
7
-
8
- import { DynamoFM_AnyError, DynamoFM_Array, DynamoFM_Error, DynamoFM_Error_Settings, DynamoFM_Log } from '@futdevpro/fsm-dynamo';
9
-
10
- export interface DynamoNTS_EmailService_Settings {
11
- host: string,
12
- port: number,
13
- email: string,
14
- pass: string,
15
- senderName: string,
16
- rootPath: string,
17
- templateComponents?: DynamoNTS_EmailTemplateComponent[],
18
- }
19
-
20
- export class DynamoNTS_EmailTemplateComponent {
21
- name: string;
22
- selector: string;
23
-
24
- templatePath?: string;
25
- template?: string;
26
-
27
- stylePath?: string;
28
- styles?: string;
29
-
30
- headerLinks?: string = '';
31
-
32
- properties?: string[] = [];
33
- subComponentSelectors?: string[] = [];
34
-
35
- constructor(
36
- set?: DynamoNTS_EmailTemplateComponent
37
- ) {
38
- if (set) {
39
- Object.assign(this, set);
40
- }
41
- }
42
- }
43
-
44
- export interface DynamoNTS_SendEmail_Settings<T = { [propertyKey: string]: string; }> {
45
- to: string;
46
- subject: string;
47
- /** direct email content, if provided, this will be used */
48
- content?: string;
49
-
50
- templateComponentName?: string;
51
- templateProperties?: T;
52
-
53
- attachments?: Attachment[];
54
- }
55
-
56
- /**
57
- *
58
- */
59
- export class DynamoNTS_EmailService {
60
- serviceName: string;
61
-
62
- private nmTransporter: NodeMailer.Transporter;
63
- private senderName: string;
64
- private senderNEmail: string;
65
-
66
- private components: DynamoNTS_EmailTemplateComponent[] = [];
67
- private componentsBySelector: {
68
- [selector: string]: DynamoNTS_EmailTemplateComponent
69
- } = {};
70
- private componentsByName: {
71
- [componentName: string]: DynamoNTS_EmailTemplateComponent
72
- } = {};
73
-
74
- private readonly styleLimitWarning: number = 16;
75
-
76
- defaultErrorUserMsg =
77
- `We encountered an uncought Email Service Error, ` +
78
- `\nplease contact the responsible development team.`;
79
-
80
- constructor (
81
- set: DynamoNTS_EmailService_Settings
82
- ) {
83
- this.serviceName = this.constructor?.name;
84
- this.senderName = set.senderName;
85
- this.senderNEmail = `${set.senderName} <${set.email}>`;
86
- this.nmTransporter = NodeMailer.createTransport({
87
- host: set.host,
88
- port: set.port,
89
- auth: {
90
- user: set.email,
91
- pass: set.pass,
92
- },
93
- });
94
-
95
- this.components = set.templateComponents ?? [];
96
- }
97
-
98
- async asyncPostConstruct(): Promise<void> {
99
- try {
100
- if (this.components) {
101
- await DynamoFM_Array.asyncForEach(
102
- this.components,
103
- async (component: DynamoNTS_EmailTemplateComponent): Promise<void> => {
104
- await this.loadComponent(component);
105
- }
106
- );
107
-
108
- this.connectComponents();
109
- }
110
-
111
- DynamoFM_Log.success(`\nEmailService construction (${this.senderName}) Finished!`);
112
- } catch (error) {
113
- throw new DynamoFM_Error({
114
- ...this._getDefaultErrorSettings('asyncPostConstruct', error, 'SYSTEM'),
115
-
116
- errorCode: 'NTS-ES0-APC0',
117
- message:
118
- `\nDynamoBEEmailService ERROR, ` +
119
- `\nThe emailService construction failed for ${this.serviceName}.`,
120
- });
121
- }
122
- }
123
-
124
- /**
125
- *
126
- * @param set
127
- */
128
- public async sendEmail<T>(
129
- set: DynamoNTS_SendEmail_Settings<T>,
130
- issuer: string
131
- ): Promise<void> {
132
- try {
133
- let content: string;
134
-
135
- if (set.content) {
136
- content = set.content;
137
-
138
- } else {
139
- if (!set.templateComponentName) {
140
- throw new DynamoFM_Error({
141
- ...this._getDefaultErrorSettings(
142
- 'sendEmail',
143
- new Error(
144
- `No email template component is given!`
145
- ),
146
- issuer
147
- ),
148
-
149
- errorCode: 'NTS-ES0-SE1',
150
- });
151
- }
152
-
153
- if (!this.componentsByName[set.templateComponentName]) {
154
- throw new DynamoFM_Error({
155
- ...this._getDefaultErrorSettings(
156
- 'sendEmail',
157
- new Error(
158
- `No email template component found with this name! (${set.templateComponentName})`
159
- ),
160
- issuer
161
- ),
162
-
163
- errorCode: 'NTS-ES0-SE2',
164
- additionalContent: {
165
- availableComponenets: Object.keys(this.componentsByName),
166
- },
167
- });
168
- }
169
-
170
- content = this.compileTemplateComponent(
171
- set.templateComponentName,
172
- set.templateProperties,
173
- issuer
174
- );
175
- }
176
-
177
- const mailOptions: MailOptions = {
178
- from: this.senderNEmail,
179
- to: set.to,
180
- subject: set.subject,
181
- html: content,
182
- attachments: set.attachments,
183
- };
184
-
185
- await new Promise<void>((resolve, reject): void => {
186
- this.nmTransporter.sendMail(mailOptions, (error): void => {
187
- if (error) {
188
- reject(error);
189
- } else {
190
- resolve();
191
- }
192
- });
193
- });
194
- } catch (error) {
195
- if ((error as Error).message?.includes?.('all recipients were rejected')) {
196
- throw new DynamoFM_Error({
197
- ...this._getDefaultErrorSettings('sendEmail', error, issuer),
198
-
199
- addECToUserMsg: false,
200
- errorCode: 'NTS-ES0-SE4',
201
- userMessage: `Can't send mail to ${set.to}!`,
202
- message: `sendEmail failed to ${set.to}`,
203
- });
204
-
205
- } else {
206
- throw new DynamoFM_Error({
207
- ...this._getDefaultErrorSettings('sendEmail', error, issuer),
208
-
209
- errorCode: 'NTS-ES0-SE0',
210
- message: `sendEmail failed to ${set.to}`,
211
- });
212
- }
213
- }
214
- }
215
-
216
- private compileTemplateComponent<T>(
217
- componentName: string,
218
- componentProperties:T,
219
- issuer: string
220
- ): string {
221
- try {
222
- if (!this.componentsByName[componentName]) {
223
- throw new DynamoFM_Error({
224
- ...this._getDefaultErrorSettings(
225
- 'setupComponent',
226
- new Error(`No email component found with this name! (${componentName})`),
227
- issuer
228
- ),
229
-
230
- errorCode: 'NTS-ES0-SC1',
231
- additionalContent: {
232
- availableComponenets: Object.keys(this.componentsByName),
233
- },
234
- });
235
- }
236
-
237
- const component: DynamoNTS_EmailTemplateComponent = this.componentsByName[componentName];
238
- let template: string = this.compileHTMLMainFrame(component);
239
-
240
- template = template.replace('<nts-content>', '');
241
- template = this.compileComponentProperties<T>(
242
- component,
243
- componentProperties,
244
- template,
245
- issuer
246
- );
247
-
248
- component.subComponentSelectors.forEach((subComponentSelector: string): void => {
249
- while (template.includes(`<nts-${subComponentSelector}`)) {
250
- template = this.compileSubComponent<T>(
251
- subComponentSelector, componentProperties, template, issuer
252
- );
253
- }
254
- });
255
-
256
- template = this.templateTrim(template);
257
-
258
- return template;
259
- } catch (error) {
260
- throw new DynamoFM_Error({
261
- ...this._getDefaultErrorSettings('setupComponent', error, issuer),
262
-
263
- errorCode: 'NTS-ES0-SC0',
264
- message: `setupComponent failed! (${componentName})`,
265
- });
266
- }
267
- }
268
-
269
- private compileHTMLMainFrame(component: DynamoNTS_EmailTemplateComponent): string {
270
- const allLinks: string[] = [];
271
- const allStyles: string[] = [];
272
-
273
- if (component.headerLinks) {
274
- allLinks.push(component.headerLinks);
275
- }
276
-
277
- if (component.styles) {
278
- allStyles.push(component.styles);
279
- }
280
-
281
- component.subComponentSelectors.forEach((subComponentSelector: string): void => {
282
- const subComponent: DynamoNTS_EmailTemplateComponent =
283
- this.componentsBySelector[subComponentSelector];
284
-
285
- if (subComponent.headerLinks) {
286
- allLinks.push(subComponent.headerLinks.trim());
287
- }
288
-
289
- if (subComponent.styles) {
290
- allStyles.push(subComponent.styles.trim());
291
- }
292
- });
293
-
294
- const styles = this.styleTrim(allStyles.join(''));
295
- const styleSize = this.getStringKBSize(styles);
296
-
297
- if (this.styleLimitWarning < styleSize) {
298
- DynamoFM_Log.warn(
299
- `\nDynamoNTS EmailService WARNING (${this.serviceName}), ` +
300
- `\nEmail template styles are too big! (${styleSize}KB)` +
301
- `\ncomponent: ${component.name} (${component.selector})` +
302
- `\nSome email clients may not support this!` +
303
- `\nThe limit is ${this.styleLimitWarning}KB!`
304
- );
305
- }
306
-
307
- return `\n<!DOCTYPE html>` +
308
- `\n<html>` +
309
- `\n <head>` +
310
- `\n ${allLinks.join('')}` +
311
- `\n` +
312
- `\n <style>` +
313
- `\n ${styles}` +
314
- `\n </style>` +
315
- `\n </head>` +
316
- `\n <body>` +
317
- `\n ${component.template}` +
318
- `\n </body>` +
319
- `\n</html> `;
320
- }
321
-
322
- /** trims all rows and remove everything between /*...*\/ */
323
- private styleTrim(style: string): string {
324
- let result: string = style.split('\n').map((row: string): string => row.trim()).join('');
325
-
326
- while (result.includes('/*')) {
327
- const start = result.indexOf('/*');
328
- const end = result.indexOf('*/', start) + 2;
329
-
330
- result = result.substring(0, start) + result.substring(end);
331
- }
332
-
333
- return result;
334
- }
335
-
336
- /** removes HTML comments */
337
- private templateTrim(template: string): string {
338
- let result: string = template;
339
-
340
- while (result.includes('<!--')) {
341
- const start = result.indexOf('<!--');
342
- const end = result.indexOf('-->', start) + 3;
343
-
344
- result = result.substring(0, start) + result.substring(end);
345
- }
346
-
347
- return result;
348
- }
349
-
350
-
351
- private getStringKBSize(str: string): number {
352
- const b: number = str.length * 2;
353
-
354
- return b / 1024;
355
- }
356
-
357
- private compileComponentProperties<T>(
358
- component: DynamoNTS_EmailTemplateComponent,
359
- componentProperties: T,
360
- template: string,
361
- issuer: string
362
- ): string {
363
- component.properties.forEach((propertyKey: string): void => {
364
- if (!componentProperties[propertyKey]) {
365
- throw new DynamoFM_Error({
366
- ...this._getDefaultErrorSettings(
367
- 'setupComponent',
368
- new Error(
369
- `ComponentProperty missing from input! '${propertyKey}' for ${component.name}`
370
- ),
371
- issuer
372
- ),
373
-
374
- errorCode: 'NTS-ES0-SC4',
375
- additionalContent: {
376
- componentProperties: Object.keys(componentProperties),
377
- },
378
- });
379
- }
380
-
381
- const propReg = new RegExp(`{{${propertyKey}}}`, 'g');
382
-
383
- template = template.replace(propReg, componentProperties[propertyKey]);
384
- });
385
-
386
- return template;
387
- }
388
-
389
- private compileSubComponent<T>(
390
- subComponentSelector: string,
391
- componentProperties: T,
392
- template: string,
393
- issuer: string
394
- ): string {
395
- const subComponent: DynamoNTS_EmailTemplateComponent =
396
- this.componentsBySelector[subComponentSelector];
397
-
398
- if (!subComponent) {
399
- throw new DynamoFM_Error({
400
- ...this._getDefaultErrorSettings(
401
- 'setupComponent',
402
- new Error(`SubComponent missing from components! (${subComponentSelector})`),
403
- issuer
404
- ),
405
-
406
- errorCode: 'NTS-ES0-SC2',
407
- additionalContent: this.componentsBySelector,
408
- });
409
- }
410
-
411
- let subComponentTemplate: string = subComponent.template;
412
- const startTag = `<nts-${subComponent.selector}`;
413
- const endTag = `</nts-${subComponent.selector}>`;
414
- const tagHTMLProperties: string =
415
- template.split(startTag)[1].split('>')[0];
416
- let componentContent: string;
417
-
418
- if (!tagHTMLProperties) {
419
- componentContent = template.split(`${startTag}>`)[1].split(endTag)[0];
420
- } else {
421
- componentContent = template.split(startTag)[1].split('>')[1].split(endTag)[0];
422
- }
423
-
424
- const contentReg = new RegExp(`<nts-content>`, 'g');
425
-
426
- subComponentTemplate = subComponentTemplate.replace(contentReg, componentContent);
427
- subComponentTemplate = this.compileComponentProperties<T>(
428
- subComponent,
429
- componentProperties,
430
- subComponentTemplate,
431
- issuer
432
- );
433
-
434
- const startTagReg = new RegExp(startTag, 'g');
435
- const endTagReg = new RegExp(endTag, 'g');
436
-
437
- template = template.replace(startTagReg, '<div');
438
- template = template.replace(componentContent, subComponentTemplate);
439
- template = template.replace(endTagReg, '</div>');
440
-
441
- /* while (template.includes(`<nts-${subComponent.selector}`)) {
442
- const replaceStartIndex = template.indexOf(`<nts-${subComponent.selector}`);
443
- const replaceEndIndex = template.indexOf(endTag, replaceStartIndex) + endTag.length;
444
-
445
- template =
446
- template.substring(0, replaceStartIndex) +
447
- subComponentCompiled +
448
- template.substring(replaceEndIndex);
449
- } */
450
-
451
- return template;
452
- }
453
-
454
- private async loadComponent(component: DynamoNTS_EmailTemplateComponent): Promise<void> {
455
- try {
456
- if (this.componentsBySelector[component.selector]) {
457
- throw new DynamoFM_Error({
458
- ...this._getDefaultErrorSettings('loadComponent', new Error(), 'SYSTEM'),
459
-
460
- errorCode: 'NTS-ES0-LC1',
461
- message: `Template already loaded! (${component.name})`,
462
- });
463
- }
464
-
465
- if (!component.templatePath && !component.template) {
466
- throw new DynamoFM_Error({
467
- ...this._getDefaultErrorSettings('loadComponent', new Error(), 'SYSTEM'),
468
-
469
- errorCode: 'NTS-ES0-LC2',
470
- message: `Component missing template and templatePath! (${component.name})`,
471
- });
472
- }
473
-
474
- if (!component.template) {
475
- await new Promise<void>((resolve, reject): void => {
476
- FileSystem.readFile(component.templatePath, 'utf8' , (err, template): void => {
477
- if (err || !template) {
478
- if (!err) {
479
- err = new Error(
480
- `Couldn't load email component's template; ${component.name} ` +
481
- `from ${component.templatePath}`
482
- );
483
- }
484
-
485
- reject(
486
- new DynamoFM_Error({
487
- ...this._getDefaultErrorSettings('loadTemplate', err, 'SYSTEM'),
488
-
489
- errorCode: 'NTS-ES0-LC3',
490
- })
491
- );
492
-
493
- return;
494
- }
495
-
496
- component.template = template;
497
-
498
- resolve();
499
- });
500
- });
501
- }
502
-
503
- if (component.stylePath) {
504
- await new Promise<void>((resolve, reject): void => {
505
- FileSystem.readFile(component.stylePath, 'utf8' , (err, styles): void => {
506
- if (err || !styles) {
507
- if (!err) {
508
- err = new Error(
509
- `Couldn't load email component's styles; ${component.name} ` +
510
- `from ${component.stylePath}`
511
- );
512
- }
513
-
514
- reject(
515
- new DynamoFM_Error({
516
- ...this._getDefaultErrorSettings('loadTemplate', err, 'SYSTEM'),
517
-
518
- errorCode: 'NTS-ES0-LC4',
519
- })
520
- );
521
-
522
- return;
523
- }
524
-
525
- component.styles = styles;
526
-
527
- resolve();
528
- });
529
- });
530
- }
531
-
532
- component.properties = this.getTemplatePropertyKeys(component.template);
533
- this.componentsBySelector[component.selector] = component;
534
- this.componentsByName[component.name] = component;
535
-
536
- component.properties.forEach((propertyKey: string): void => {
537
- if (!component.template.includes(`{{${propertyKey}}}`)) {
538
- throw new DynamoFM_Error({
539
- ...this._getDefaultErrorSettings(
540
- 'loadComponent',
541
- new Error(
542
- `TemplateProperty missing from template! (${propertyKey} for ${component.name})`
543
- ),
544
- 'SYSTEM'
545
- ),
546
-
547
- errorCode: 'NTS-ES0-LC5',
548
- additionalContent: component.template,
549
- });
550
- }
551
- });
552
- } catch (error) {
553
- throw new DynamoFM_Error({
554
- ...this._getDefaultErrorSettings('loadComponent', error, 'SYSTEM'),
555
-
556
- errorCode: 'NTS-ES0-LC0',
557
- message: `loadComponent failed! (${component.name})`,
558
- });
559
- }
560
- }
561
-
562
- private connectComponents(): void {
563
- try {
564
- for (const componentSelector in this.componentsBySelector) {
565
- this.componentsBySelector[componentSelector].subComponentSelectors = [];
566
-
567
- const innerTags: string[] =
568
- this.componentsBySelector[componentSelector].template.split('<nts-');
569
-
570
- innerTags.shift();
571
- innerTags.forEach(
572
- (subComponents: string): void => {
573
- const subComponentSelector = subComponents.split('>')[0].split(' ')[0];
574
-
575
- if (subComponentSelector !== 'content') {
576
- const fullSelector = 'nts-' + subComponentSelector;
577
-
578
- if (fullSelector) {
579
- if (!this.componentsBySelector[subComponentSelector]) {
580
- throw new DynamoFM_Error({
581
- ...this._getDefaultErrorSettings(
582
- 'connectComponents',
583
- new Error(`SubComponent missing from components! (${fullSelector})`),
584
- 'SYSTEM'
585
- ),
586
-
587
- errorCode: 'NTS-ES0-CC1',
588
- additionalContent: {
589
- availableComponenets: Object.keys(this.componentsBySelector),
590
- },
591
- });
592
- }
593
-
594
- this.componentsBySelector[componentSelector].subComponentSelectors.push(
595
- subComponentSelector
596
- );
597
- }
598
- }
599
- }
600
- );
601
- }
602
- } catch (error) {
603
- throw new DynamoFM_Error({
604
- ...this._getDefaultErrorSettings('connectComponents', error, 'SYSTEM'),
605
-
606
- errorCode: 'NTS-ES0-CC0',
607
- message: `connectComponents failed!`,
608
- });
609
- }
610
- }
611
-
612
- /**
613
- *
614
- * @param template
615
- * @returns
616
- */
617
- private getTemplatePropertyKeys(template: string): string[] {
618
- try {
619
- const propertyKeys: string[] = [];
620
- let propertyOpenTagIndex = template.indexOf('{{');
621
- let propertyCloseTagIndex = template.indexOf('}}', propertyOpenTagIndex + 2);
622
-
623
- while (propertyOpenTagIndex >= 0) {
624
- if (propertyCloseTagIndex === -1) {
625
- DynamoFM_Log.error(
626
- `\nDynamoBEEmailService ERROR, missing closing tag from email template! ` +
627
- `(${propertyOpenTagIndex} -)`,
628
- propertyKeys
629
- );
630
-
631
- throw new DynamoFM_Error({
632
- errorCode: 'NTS-ES0-200',
633
- addECToUserMsg: true,
634
- message: `ERROR, missing closing tag from email template! (${propertyOpenTagIndex} -)`,
635
- userMessage: this.defaultErrorUserMsg,
636
- });
637
- }
638
-
639
- const newKey: string = template.substring(propertyOpenTagIndex + 2, propertyCloseTagIndex);
640
-
641
- if (!propertyKeys.includes(newKey)) {
642
- propertyKeys.push(newKey);
643
- }
644
-
645
- // KELLEZIDE?
646
- propertyOpenTagIndex = template.indexOf('{{', propertyOpenTagIndex + 2);
647
- propertyCloseTagIndex = template.indexOf('}}', propertyOpenTagIndex + 2);
648
- }
649
- // console.log('\n\n\nTEST propertyKeys: ', propertyKeys);
650
-
651
- return propertyKeys;
652
- } catch (error) {
653
- throw new DynamoFM_Error({
654
- ...this._getDefaultErrorSettings('getTemplatePropertyKeys', error, 'SYSTEM'),
655
-
656
- errorCode: 'NTS-ES0-GTPK0',
657
- message: `getTemplatePropertyKeys failed!`,
658
- additionalContent: template,
659
- });
660
- }
661
- }
662
-
663
- private _getDefaultErrorSettings(
664
- fnName: string,
665
- error: DynamoFM_AnyError,
666
- issuer: string
667
- ): DynamoFM_Error_Settings {
668
- return {
669
- status: (error as DynamoFM_Error)?.___status ?? 500,
670
- message: (error as Error)?.message ?? `${fnName} was UNSUCCESFUL (NTS; ${this.serviceName})`,
671
- addECToUserMsg: !(error as DynamoFM_Error)?.__userMessage,
672
- userMessage: (error as DynamoFM_Error)?.__userMessage ?? this.defaultErrorUserMsg,
673
- issuer: issuer,
674
- issuerService: this.serviceName,
675
- error: error,
676
- };
677
- }
678
- }
1
+
2
+ import * as FileSystem from 'fs';
3
+ import * as Path from 'path';
4
+ import * as NodeMailer from 'nodemailer';
5
+
6
+ import { Options as MailOptions, Attachment } from 'nodemailer/lib/mailer';
7
+
8
+ import { DynamoFM_AnyError, DynamoFM_Array, DynamoFM_Error, DynamoFM_Error_Settings, DynamoFM_Log } from '@futdevpro/fsm-dynamo';
9
+
10
+ export interface DynamoNTS_EmailService_Settings {
11
+ host: string,
12
+ port: number,
13
+ email: string,
14
+ pass: string,
15
+ senderName: string,
16
+ rootPath: string,
17
+ templateComponents?: DynamoNTS_EmailTemplateComponent[],
18
+ }
19
+
20
+ export class DynamoNTS_EmailTemplateComponent {
21
+ name: string;
22
+ selector: string;
23
+
24
+ templatePath?: string;
25
+ template?: string;
26
+
27
+ stylePath?: string;
28
+ styles?: string;
29
+
30
+ headerLinks?: string = '';
31
+
32
+ properties?: string[] = [];
33
+ subComponentSelectors?: string[] = [];
34
+
35
+ constructor(
36
+ set?: DynamoNTS_EmailTemplateComponent
37
+ ) {
38
+ if (set) {
39
+ Object.assign(this, set);
40
+ }
41
+ }
42
+ }
43
+
44
+ export interface DynamoNTS_SendEmail_Settings<T = { [propertyKey: string]: string; }> {
45
+ to: string;
46
+ subject: string;
47
+ /** direct email content, if provided, this will be used */
48
+ content?: string;
49
+
50
+ templateComponentName?: string;
51
+ templateProperties?: T;
52
+
53
+ attachments?: Attachment[];
54
+ }
55
+
56
+ /**
57
+ *
58
+ */
59
+ export class DynamoNTS_EmailService {
60
+ serviceName: string;
61
+
62
+ private nmTransporter: NodeMailer.Transporter;
63
+ private senderName: string;
64
+ private senderNEmail: string;
65
+
66
+ private components: DynamoNTS_EmailTemplateComponent[] = [];
67
+ private componentsBySelector: {
68
+ [selector: string]: DynamoNTS_EmailTemplateComponent
69
+ } = {};
70
+ private componentsByName: {
71
+ [componentName: string]: DynamoNTS_EmailTemplateComponent
72
+ } = {};
73
+
74
+ private readonly styleLimitWarning: number = 16;
75
+
76
+ defaultErrorUserMsg =
77
+ `We encountered an uncought Email Service Error, ` +
78
+ `\nplease contact the responsible development team.`;
79
+
80
+ constructor (
81
+ set: DynamoNTS_EmailService_Settings
82
+ ) {
83
+ this.serviceName = this.constructor?.name;
84
+ this.senderName = set.senderName;
85
+ this.senderNEmail = `${set.senderName} <${set.email}>`;
86
+ this.nmTransporter = NodeMailer.createTransport({
87
+ host: set.host,
88
+ port: set.port,
89
+ auth: {
90
+ user: set.email,
91
+ pass: set.pass,
92
+ },
93
+ });
94
+
95
+ this.components = set.templateComponents ?? [];
96
+ }
97
+
98
+ async asyncPostConstruct(): Promise<void> {
99
+ try {
100
+ if (this.components) {
101
+ await DynamoFM_Array.asyncForEach(
102
+ this.components,
103
+ async (component: DynamoNTS_EmailTemplateComponent): Promise<void> => {
104
+ await this.loadComponent(component);
105
+ }
106
+ );
107
+
108
+ this.connectComponents();
109
+ }
110
+
111
+ DynamoFM_Log.success(`\nEmailService construction (${this.senderName}) Finished!`);
112
+ } catch (error) {
113
+ throw new DynamoFM_Error({
114
+ ...this._getDefaultErrorSettings('asyncPostConstruct', error, 'SYSTEM'),
115
+
116
+ errorCode: 'NTS-ES0-APC0',
117
+ message:
118
+ `\nDynamoBEEmailService ERROR, ` +
119
+ `\nThe emailService construction failed for ${this.serviceName}.`,
120
+ });
121
+ }
122
+ }
123
+
124
+ /**
125
+ *
126
+ * @param set
127
+ */
128
+ public async sendEmail<T>(
129
+ set: DynamoNTS_SendEmail_Settings<T>,
130
+ issuer: string
131
+ ): Promise<void> {
132
+ try {
133
+ let content: string;
134
+
135
+ if (set.content) {
136
+ content = set.content;
137
+
138
+ } else {
139
+ if (!set.templateComponentName) {
140
+ throw new DynamoFM_Error({
141
+ ...this._getDefaultErrorSettings(
142
+ 'sendEmail',
143
+ new Error(
144
+ `No email template component is given!`
145
+ ),
146
+ issuer
147
+ ),
148
+
149
+ errorCode: 'NTS-ES0-SE1',
150
+ });
151
+ }
152
+
153
+ if (!this.componentsByName[set.templateComponentName]) {
154
+ throw new DynamoFM_Error({
155
+ ...this._getDefaultErrorSettings(
156
+ 'sendEmail',
157
+ new Error(
158
+ `No email template component found with this name! (${set.templateComponentName})`
159
+ ),
160
+ issuer
161
+ ),
162
+
163
+ errorCode: 'NTS-ES0-SE2',
164
+ additionalContent: {
165
+ availableComponenets: Object.keys(this.componentsByName),
166
+ },
167
+ });
168
+ }
169
+
170
+ content = this.compileTemplateComponent(
171
+ set.templateComponentName,
172
+ set.templateProperties,
173
+ issuer
174
+ );
175
+ }
176
+
177
+ const mailOptions: MailOptions = {
178
+ from: this.senderNEmail,
179
+ to: set.to,
180
+ subject: set.subject,
181
+ html: content,
182
+ attachments: set.attachments,
183
+ };
184
+
185
+ await new Promise<void>((resolve, reject): void => {
186
+ this.nmTransporter.sendMail(mailOptions, (error): void => {
187
+ if (error) {
188
+ reject(error);
189
+ } else {
190
+ resolve();
191
+ }
192
+ });
193
+ });
194
+ } catch (error) {
195
+ if ((error as Error).message?.includes?.('all recipients were rejected')) {
196
+ throw new DynamoFM_Error({
197
+ ...this._getDefaultErrorSettings('sendEmail', error, issuer),
198
+
199
+ addECToUserMsg: false,
200
+ errorCode: 'NTS-ES0-SE4',
201
+ userMessage: `Can't send mail to ${set.to}!`,
202
+ message: `sendEmail failed to ${set.to}`,
203
+ });
204
+
205
+ } else {
206
+ throw new DynamoFM_Error({
207
+ ...this._getDefaultErrorSettings('sendEmail', error, issuer),
208
+
209
+ errorCode: 'NTS-ES0-SE0',
210
+ message: `sendEmail failed to ${set.to}`,
211
+ });
212
+ }
213
+ }
214
+ }
215
+
216
+ private compileTemplateComponent<T>(
217
+ componentName: string,
218
+ componentProperties:T,
219
+ issuer: string
220
+ ): string {
221
+ try {
222
+ if (!this.componentsByName[componentName]) {
223
+ throw new DynamoFM_Error({
224
+ ...this._getDefaultErrorSettings(
225
+ 'setupComponent',
226
+ new Error(`No email component found with this name! (${componentName})`),
227
+ issuer
228
+ ),
229
+
230
+ errorCode: 'NTS-ES0-SC1',
231
+ additionalContent: {
232
+ availableComponenets: Object.keys(this.componentsByName),
233
+ },
234
+ });
235
+ }
236
+
237
+ const component: DynamoNTS_EmailTemplateComponent = this.componentsByName[componentName];
238
+ let template: string = this.compileHTMLMainFrame(component);
239
+
240
+ template = template.replace('<nts-content>', '');
241
+ template = this.compileComponentProperties<T>(
242
+ component,
243
+ componentProperties,
244
+ template,
245
+ issuer
246
+ );
247
+
248
+ component.subComponentSelectors.forEach((subComponentSelector: string): void => {
249
+ while (template.includes(`<nts-${subComponentSelector}`)) {
250
+ template = this.compileSubComponent<T>(
251
+ subComponentSelector, componentProperties, template, issuer
252
+ );
253
+ }
254
+ });
255
+
256
+ template = this.templateTrim(template);
257
+
258
+ return template;
259
+ } catch (error) {
260
+ throw new DynamoFM_Error({
261
+ ...this._getDefaultErrorSettings('setupComponent', error, issuer),
262
+
263
+ errorCode: 'NTS-ES0-SC0',
264
+ message: `setupComponent failed! (${componentName})`,
265
+ });
266
+ }
267
+ }
268
+
269
+ private compileHTMLMainFrame(component: DynamoNTS_EmailTemplateComponent): string {
270
+ const allLinks: string[] = [];
271
+ const allStyles: string[] = [];
272
+
273
+ if (component.headerLinks) {
274
+ allLinks.push(component.headerLinks);
275
+ }
276
+
277
+ if (component.styles) {
278
+ allStyles.push(component.styles);
279
+ }
280
+
281
+ component.subComponentSelectors.forEach((subComponentSelector: string): void => {
282
+ const subComponent: DynamoNTS_EmailTemplateComponent =
283
+ this.componentsBySelector[subComponentSelector];
284
+
285
+ if (subComponent.headerLinks) {
286
+ allLinks.push(subComponent.headerLinks.trim());
287
+ }
288
+
289
+ if (subComponent.styles) {
290
+ allStyles.push(subComponent.styles.trim());
291
+ }
292
+ });
293
+
294
+ const styles = this.styleTrim(allStyles.join(''));
295
+ const styleSize = this.getStringKBSize(styles);
296
+
297
+ if (this.styleLimitWarning < styleSize) {
298
+ DynamoFM_Log.warn(
299
+ `\nDynamoNTS EmailService WARNING (${this.serviceName}), ` +
300
+ `\nEmail template styles are too big! (${styleSize}KB)` +
301
+ `\ncomponent: ${component.name} (${component.selector})` +
302
+ `\nSome email clients may not support this!` +
303
+ `\nThe limit is ${this.styleLimitWarning}KB!`
304
+ );
305
+ }
306
+
307
+ return `\n<!DOCTYPE html>` +
308
+ `\n<html>` +
309
+ `\n <head>` +
310
+ `\n ${allLinks.join('')}` +
311
+ `\n` +
312
+ `\n <style>` +
313
+ `\n ${styles}` +
314
+ `\n </style>` +
315
+ `\n </head>` +
316
+ `\n <body>` +
317
+ `\n ${component.template}` +
318
+ `\n </body>` +
319
+ `\n</html> `;
320
+ }
321
+
322
+ /** trims all rows and remove everything between /*...*\/ */
323
+ private styleTrim(style: string): string {
324
+ let result: string = style.split('\n').map((row: string): string => row.trim()).join('');
325
+
326
+ while (result.includes('/*')) {
327
+ const start = result.indexOf('/*');
328
+ const end = result.indexOf('*/', start) + 2;
329
+
330
+ result = result.substring(0, start) + result.substring(end);
331
+ }
332
+
333
+ return result;
334
+ }
335
+
336
+ /** removes HTML comments */
337
+ private templateTrim(template: string): string {
338
+ let result: string = template;
339
+
340
+ while (result.includes('<!--')) {
341
+ const start = result.indexOf('<!--');
342
+ const end = result.indexOf('-->', start) + 3;
343
+
344
+ result = result.substring(0, start) + result.substring(end);
345
+ }
346
+
347
+ return result;
348
+ }
349
+
350
+
351
+ private getStringKBSize(str: string): number {
352
+ const b: number = str.length * 2;
353
+
354
+ return b / 1024;
355
+ }
356
+
357
+ private compileComponentProperties<T>(
358
+ component: DynamoNTS_EmailTemplateComponent,
359
+ componentProperties: T,
360
+ template: string,
361
+ issuer: string
362
+ ): string {
363
+ component.properties.forEach((propertyKey: string): void => {
364
+ if (!componentProperties[propertyKey]) {
365
+ throw new DynamoFM_Error({
366
+ ...this._getDefaultErrorSettings(
367
+ 'setupComponent',
368
+ new Error(
369
+ `ComponentProperty missing from input! '${propertyKey}' for ${component.name}`
370
+ ),
371
+ issuer
372
+ ),
373
+
374
+ errorCode: 'NTS-ES0-SC4',
375
+ additionalContent: {
376
+ componentProperties: Object.keys(componentProperties),
377
+ },
378
+ });
379
+ }
380
+
381
+ const propReg = new RegExp(`{{${propertyKey}}}`, 'g');
382
+
383
+ template = template.replace(propReg, componentProperties[propertyKey]);
384
+ });
385
+
386
+ return template;
387
+ }
388
+
389
+ private compileSubComponent<T>(
390
+ subComponentSelector: string,
391
+ componentProperties: T,
392
+ template: string,
393
+ issuer: string
394
+ ): string {
395
+ const subComponent: DynamoNTS_EmailTemplateComponent =
396
+ this.componentsBySelector[subComponentSelector];
397
+
398
+ if (!subComponent) {
399
+ throw new DynamoFM_Error({
400
+ ...this._getDefaultErrorSettings(
401
+ 'setupComponent',
402
+ new Error(`SubComponent missing from components! (${subComponentSelector})`),
403
+ issuer
404
+ ),
405
+
406
+ errorCode: 'NTS-ES0-SC2',
407
+ additionalContent: this.componentsBySelector,
408
+ });
409
+ }
410
+
411
+ let subComponentTemplate: string = subComponent.template;
412
+ const startTag = `<nts-${subComponent.selector}`;
413
+ const endTag = `</nts-${subComponent.selector}>`;
414
+ const tagHTMLProperties: string =
415
+ template.split(startTag)[1].split('>')[0];
416
+ let componentContent: string;
417
+
418
+ if (!tagHTMLProperties) {
419
+ componentContent = template.split(`${startTag}>`)[1].split(endTag)[0];
420
+ } else {
421
+ componentContent = template.split(startTag)[1].split('>')[1].split(endTag)[0];
422
+ }
423
+
424
+ const contentReg = new RegExp(`<nts-content>`, 'g');
425
+
426
+ subComponentTemplate = subComponentTemplate.replace(contentReg, componentContent);
427
+ subComponentTemplate = this.compileComponentProperties<T>(
428
+ subComponent,
429
+ componentProperties,
430
+ subComponentTemplate,
431
+ issuer
432
+ );
433
+
434
+ const startTagReg = new RegExp(startTag, 'g');
435
+ const endTagReg = new RegExp(endTag, 'g');
436
+
437
+ template = template.replace(startTagReg, '<div');
438
+ template = template.replace(componentContent, subComponentTemplate);
439
+ template = template.replace(endTagReg, '</div>');
440
+
441
+ /* while (template.includes(`<nts-${subComponent.selector}`)) {
442
+ const replaceStartIndex = template.indexOf(`<nts-${subComponent.selector}`);
443
+ const replaceEndIndex = template.indexOf(endTag, replaceStartIndex) + endTag.length;
444
+
445
+ template =
446
+ template.substring(0, replaceStartIndex) +
447
+ subComponentCompiled +
448
+ template.substring(replaceEndIndex);
449
+ } */
450
+
451
+ return template;
452
+ }
453
+
454
+ private async loadComponent(component: DynamoNTS_EmailTemplateComponent): Promise<void> {
455
+ try {
456
+ if (this.componentsBySelector[component.selector]) {
457
+ throw new DynamoFM_Error({
458
+ ...this._getDefaultErrorSettings('loadComponent', new Error(), 'SYSTEM'),
459
+
460
+ errorCode: 'NTS-ES0-LC1',
461
+ message: `Template already loaded! (${component.name})`,
462
+ });
463
+ }
464
+
465
+ if (!component.templatePath && !component.template) {
466
+ throw new DynamoFM_Error({
467
+ ...this._getDefaultErrorSettings('loadComponent', new Error(), 'SYSTEM'),
468
+
469
+ errorCode: 'NTS-ES0-LC2',
470
+ message: `Component missing template and templatePath! (${component.name})`,
471
+ });
472
+ }
473
+
474
+ if (!component.template) {
475
+ await new Promise<void>((resolve, reject): void => {
476
+ FileSystem.readFile(component.templatePath, 'utf8' , (err, template): void => {
477
+ if (err || !template) {
478
+ if (!err) {
479
+ err = new Error(
480
+ `Couldn't load email component's template; ${component.name} ` +
481
+ `from ${component.templatePath}`
482
+ );
483
+ }
484
+
485
+ reject(
486
+ new DynamoFM_Error({
487
+ ...this._getDefaultErrorSettings('loadTemplate', err, 'SYSTEM'),
488
+
489
+ errorCode: 'NTS-ES0-LC3',
490
+ })
491
+ );
492
+
493
+ return;
494
+ }
495
+
496
+ component.template = template;
497
+
498
+ resolve();
499
+ });
500
+ });
501
+ }
502
+
503
+ if (component.stylePath) {
504
+ await new Promise<void>((resolve, reject): void => {
505
+ FileSystem.readFile(component.stylePath, 'utf8' , (err, styles): void => {
506
+ if (err || !styles) {
507
+ if (!err) {
508
+ err = new Error(
509
+ `Couldn't load email component's styles; ${component.name} ` +
510
+ `from ${component.stylePath}`
511
+ );
512
+ }
513
+
514
+ reject(
515
+ new DynamoFM_Error({
516
+ ...this._getDefaultErrorSettings('loadTemplate', err, 'SYSTEM'),
517
+
518
+ errorCode: 'NTS-ES0-LC4',
519
+ })
520
+ );
521
+
522
+ return;
523
+ }
524
+
525
+ component.styles = styles;
526
+
527
+ resolve();
528
+ });
529
+ });
530
+ }
531
+
532
+ component.properties = this.getTemplatePropertyKeys(component.template);
533
+ this.componentsBySelector[component.selector] = component;
534
+ this.componentsByName[component.name] = component;
535
+
536
+ component.properties.forEach((propertyKey: string): void => {
537
+ if (!component.template.includes(`{{${propertyKey}}}`)) {
538
+ throw new DynamoFM_Error({
539
+ ...this._getDefaultErrorSettings(
540
+ 'loadComponent',
541
+ new Error(
542
+ `TemplateProperty missing from template! (${propertyKey} for ${component.name})`
543
+ ),
544
+ 'SYSTEM'
545
+ ),
546
+
547
+ errorCode: 'NTS-ES0-LC5',
548
+ additionalContent: component.template,
549
+ });
550
+ }
551
+ });
552
+ } catch (error) {
553
+ throw new DynamoFM_Error({
554
+ ...this._getDefaultErrorSettings('loadComponent', error, 'SYSTEM'),
555
+
556
+ errorCode: 'NTS-ES0-LC0',
557
+ message: `loadComponent failed! (${component.name})`,
558
+ });
559
+ }
560
+ }
561
+
562
+ private connectComponents(): void {
563
+ try {
564
+ for (const componentSelector in this.componentsBySelector) {
565
+ this.componentsBySelector[componentSelector].subComponentSelectors = [];
566
+
567
+ const innerTags: string[] =
568
+ this.componentsBySelector[componentSelector].template.split('<nts-');
569
+
570
+ innerTags.shift();
571
+ innerTags.forEach(
572
+ (subComponents: string): void => {
573
+ const subComponentSelector = subComponents.split('>')[0].split(' ')[0];
574
+
575
+ if (subComponentSelector !== 'content') {
576
+ const fullSelector = 'nts-' + subComponentSelector;
577
+
578
+ if (fullSelector) {
579
+ if (!this.componentsBySelector[subComponentSelector]) {
580
+ throw new DynamoFM_Error({
581
+ ...this._getDefaultErrorSettings(
582
+ 'connectComponents',
583
+ new Error(`SubComponent missing from components! (${fullSelector})`),
584
+ 'SYSTEM'
585
+ ),
586
+
587
+ errorCode: 'NTS-ES0-CC1',
588
+ additionalContent: {
589
+ availableComponenets: Object.keys(this.componentsBySelector),
590
+ },
591
+ });
592
+ }
593
+
594
+ this.componentsBySelector[componentSelector].subComponentSelectors.push(
595
+ subComponentSelector
596
+ );
597
+ }
598
+ }
599
+ }
600
+ );
601
+ }
602
+ } catch (error) {
603
+ throw new DynamoFM_Error({
604
+ ...this._getDefaultErrorSettings('connectComponents', error, 'SYSTEM'),
605
+
606
+ errorCode: 'NTS-ES0-CC0',
607
+ message: `connectComponents failed!`,
608
+ });
609
+ }
610
+ }
611
+
612
+ /**
613
+ *
614
+ * @param template
615
+ * @returns
616
+ */
617
+ private getTemplatePropertyKeys(template: string): string[] {
618
+ try {
619
+ const propertyKeys: string[] = [];
620
+ let propertyOpenTagIndex = template.indexOf('{{');
621
+ let propertyCloseTagIndex = template.indexOf('}}', propertyOpenTagIndex + 2);
622
+
623
+ while (propertyOpenTagIndex >= 0) {
624
+ if (propertyCloseTagIndex === -1) {
625
+ DynamoFM_Log.error(
626
+ `\nDynamoBEEmailService ERROR, missing closing tag from email template! ` +
627
+ `(${propertyOpenTagIndex} -)`,
628
+ propertyKeys
629
+ );
630
+
631
+ throw new DynamoFM_Error({
632
+ errorCode: 'NTS-ES0-200',
633
+ addECToUserMsg: true,
634
+ message: `ERROR, missing closing tag from email template! (${propertyOpenTagIndex} -)`,
635
+ userMessage: this.defaultErrorUserMsg,
636
+ });
637
+ }
638
+
639
+ const newKey: string = template.substring(propertyOpenTagIndex + 2, propertyCloseTagIndex);
640
+
641
+ if (!propertyKeys.includes(newKey)) {
642
+ propertyKeys.push(newKey);
643
+ }
644
+
645
+ // KELLEZIDE?
646
+ propertyOpenTagIndex = template.indexOf('{{', propertyOpenTagIndex + 2);
647
+ propertyCloseTagIndex = template.indexOf('}}', propertyOpenTagIndex + 2);
648
+ }
649
+ // console.log('\n\n\nTEST propertyKeys: ', propertyKeys);
650
+
651
+ return propertyKeys;
652
+ } catch (error) {
653
+ throw new DynamoFM_Error({
654
+ ...this._getDefaultErrorSettings('getTemplatePropertyKeys', error, 'SYSTEM'),
655
+
656
+ errorCode: 'NTS-ES0-GTPK0',
657
+ message: `getTemplatePropertyKeys failed!`,
658
+ additionalContent: template,
659
+ });
660
+ }
661
+ }
662
+
663
+ private _getDefaultErrorSettings(
664
+ fnName: string,
665
+ error: DynamoFM_AnyError,
666
+ issuer: string
667
+ ): DynamoFM_Error_Settings {
668
+ return {
669
+ status: (error as DynamoFM_Error)?.___status ?? 500,
670
+ message: (error as Error)?.message ?? `${fnName} was UNSUCCESFUL (NTS; ${this.serviceName})`,
671
+ addECToUserMsg: !(error as DynamoFM_Error)?.__userMessage,
672
+ userMessage: (error as DynamoFM_Error)?.__userMessage ?? this.defaultErrorUserMsg,
673
+ issuer: issuer,
674
+ issuerService: this.serviceName,
675
+ error: error,
676
+ };
677
+ }
678
+ }