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