@futdevpro/nts-dynamo 1.9.15 → 1.9.17

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 (132) hide show
  1. package/.copilot/patterns.json +7 -7
  2. package/.github/workflows/main.yml +221 -0
  3. package/HOWTO.md +15 -15
  4. package/README.md +140 -140
  5. package/build/_constants/mocks/auth-service.mock.d.ts.map +1 -1
  6. package/build/_models/control-models/app-params.control-model.d.ts +1 -0
  7. package/build/_models/control-models/app-params.control-model.d.ts.map +1 -1
  8. package/build/_models/control-models/app-params.control-model.js.map +1 -1
  9. package/build/_models/control-models/endpoint-params.control-model.d.ts.map +1 -1
  10. package/build/_models/control-models/endpoint-params.control-model.js +2 -0
  11. package/build/_models/control-models/endpoint-params.control-model.js.map +1 -1
  12. package/build/_models/control-models/socket-event.control-model.js +1 -1
  13. package/build/_models/interfaces/certification-settings.interface.d.ts +0 -1
  14. package/build/_models/interfaces/certification-settings.interface.d.ts.map +1 -1
  15. package/build/_models/interfaces/global-service-settings.interface.d.ts +1 -1
  16. package/build/_models/interfaces/global-service-settings.interface.d.ts.map +1 -1
  17. package/build/_modules/custom-data/get-custom-data-routing-module.util.js +1 -2
  18. package/build/_modules/custom-data/get-custom-data-routing-module.util.js.map +1 -1
  19. package/build/_modules/test/get-test-routing-module.util.js +1 -2
  20. package/build/_modules/test/get-test-routing-module.util.js.map +1 -1
  21. package/build/_modules/usage/get-usage-routing-module.util.js +1 -2
  22. package/build/_modules/usage/get-usage-routing-module.util.js.map +1 -1
  23. package/build/_services/core/global.service.d.ts.map +1 -1
  24. package/build/_services/core/global.service.js +14 -6
  25. package/build/_services/core/global.service.js.map +1 -1
  26. package/build/_services/route/routing-module.service.d.ts +1 -1
  27. package/build/_services/route/routing-module.service.d.ts.map +1 -1
  28. package/build/_services/route/routing-module.service.js +19 -25
  29. package/build/_services/route/routing-module.service.js.map +1 -1
  30. package/build/_services/server/app.server.d.ts +0 -2
  31. package/build/_services/server/app.server.d.ts.map +1 -1
  32. package/build/_services/server/app.server.js +4 -1
  33. package/build/_services/server/app.server.js.map +1 -1
  34. package/build/_services/socket/socket-client.service.d.ts.map +1 -1
  35. package/build/_services/socket/socket-client.service.js +1 -0
  36. package/build/_services/socket/socket-client.service.js.map +1 -1
  37. package/nodemon.json +17 -15
  38. package/package.json +5 -5
  39. package/src/_constants/global-settings.const.ts +27 -27
  40. package/src/_constants/index.ts +2 -2
  41. package/src/_constants/mocks/app-extended-server.mock.ts +198 -198
  42. package/src/_constants/mocks/app-params.mock.ts +9 -9
  43. package/src/_constants/mocks/app-server.mock.ts +185 -185
  44. package/src/_constants/mocks/auth-service.mock.ts +28 -28
  45. package/src/_constants/mocks/controller.mock.ts +16 -16
  46. package/src/_constants/mocks/data-model.mock.ts +83 -83
  47. package/src/_constants/mocks/email-service-collection.mock.ts +13 -13
  48. package/src/_constants/mocks/email-service.mock.ts +19 -19
  49. package/src/_constants/mocks/email-template.mock.html +14 -14
  50. package/src/_constants/mocks/endpoint.mock.ts +90 -90
  51. package/src/_constants/mocks/socket-client.mock.ts +43 -43
  52. package/src/_constants/mocks/socket-server.mock.ts +43 -43
  53. package/src/_enums/data-model-type.enum.ts +14 -14
  54. package/src/_enums/data-service-function.enum.ts +15 -15
  55. package/src/_enums/http/http-call-type.enum.ts +12 -12
  56. package/src/_enums/http/http-response-type.enum.ts +7 -7
  57. package/src/_enums/http/socket-event-type.enum.ts +18 -18
  58. package/src/_enums/index.ts +13 -13
  59. package/src/_enums/predefined-data-types.enum.ts +27 -27
  60. package/src/_enums/route-security.enum.ts +12 -12
  61. package/src/_enums/socket-security.enum.ts +11 -11
  62. package/src/_models/control-models/api-call-params.control-model.ts +126 -126
  63. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  64. package/src/_models/control-models/app-params.control-model.ts +46 -45
  65. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  66. package/src/_models/control-models/endpoint-params.control-model.ts +309 -307
  67. package/src/_models/control-models/http-settings.control-model.ts +29 -29
  68. package/src/_models/control-models/index.ts +13 -13
  69. package/src/_models/control-models/socket-client-service-params.control-model.ts +28 -28
  70. package/src/_models/control-models/socket-event.control-model.ts +150 -150
  71. package/src/_models/control-models/socket-presence.control-model.ts +207 -207
  72. package/src/_models/control-models/socket-server-service-params.control-model.ts +20 -20
  73. package/src/_models/control-models/system-control.control-model.ts +12 -12
  74. package/src/_models/index.ts +9 -9
  75. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  76. package/src/_models/interfaces/global-service-settings.interface.ts +45 -45
  77. package/src/_models/interfaces/global-settings.interface.ts +83 -83
  78. package/src/_models/interfaces/index.ts +7 -7
  79. package/src/_models/interfaces/routing-module-settings.interface.ts +20 -20
  80. package/src/_models/types/db-filter.type.ts +108 -108
  81. package/src/_models/types/db-update.type.ts +100 -100
  82. package/src/_models/types/index.ts +5 -5
  83. package/src/_modules/api-service.index.ts +12 -12
  84. package/src/_modules/app-extended.index.ts +28 -28
  85. package/src/_modules/app.index.ts +24 -24
  86. package/src/_modules/auth.index.ts +7 -7
  87. package/src/_modules/constants.index.ts +2 -2
  88. package/src/_modules/controller.index.ts +10 -10
  89. package/src/_modules/custom-data/custom-data.controller.ts +69 -69
  90. package/src/_modules/custom-data/custom-data.data-service.ts +20 -20
  91. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +23 -23
  92. package/src/_modules/custom-data/index.ts +6 -6
  93. package/src/_modules/custom-data-module.index.ts +2 -2
  94. package/src/_modules/data-service.index.ts +9 -9
  95. package/src/_modules/email.index.ts +8 -8
  96. package/src/_modules/enums.index.ts +2 -2
  97. package/src/_modules/extended.index.ts +8 -8
  98. package/src/_modules/models.index.ts +2 -2
  99. package/src/_modules/services.index.ts +2 -2
  100. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  101. package/src/_modules/test/index.ts +5 -5
  102. package/src/_modules/test/test.controller.ts +115 -115
  103. package/src/_modules/test-module.index.ts +2 -2
  104. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  105. package/src/_modules/usage/index.ts +7 -7
  106. package/src/_modules/usage/usage.controller.ts +120 -120
  107. package/src/_modules/usage/usage.data-service.ts +172 -172
  108. package/src/_modules/usage-module.index.ts +2 -2
  109. package/src/_services/base/data.service.ts +921 -921
  110. package/src/_services/base/db.service.spec.ts +32 -32
  111. package/src/_services/base/db.service.ts +1063 -1063
  112. package/src/_services/base/singleton.service.ts +21 -21
  113. package/src/_services/core/api.service.ts +453 -453
  114. package/src/_services/core/auth.service.ts +172 -172
  115. package/src/_services/core/email.service.ts +678 -678
  116. package/src/_services/core/global.service.ts +275 -269
  117. package/src/_services/core/service-collection.service.ts +5 -5
  118. package/src/_services/index.ts +23 -23
  119. package/src/_services/route/controller.service.ts +129 -129
  120. package/src/_services/route/routing-module.service.ts +293 -273
  121. package/src/_services/server/app-extended.server.spec.ts +76 -76
  122. package/src/_services/server/app-extended.server.ts +520 -520
  123. package/src/_services/server/app.server.spec.ts +67 -67
  124. package/src/_services/server/app.server.ts +1181 -1179
  125. package/src/_services/shared.service.spec.ts +19 -19
  126. package/src/_services/shared.static-service.ts +73 -73
  127. package/src/_services/socket/socket-client.service.ts +236 -235
  128. package/src/_services/socket/socket-server.service.spec.ts +11 -11
  129. package/src/_services/socket/socket-server.service.ts +761 -761
  130. package/src/index.ts +18 -18
  131. package/tsconfig.json +41 -41
  132. package/build/tsconfig.tsbuildinfo +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
+ }