@twin.org/messaging-service 0.9.2 → 0.9.3-next.1

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.
@@ -75,14 +75,24 @@ export class MessagingAdminService {
75
75
  // First try to get the template for the requested locale
76
76
  templateEntry = await this._entityStorageConnector.get(`${templateId}:${locale}`);
77
77
  }
78
- catch { }
78
+ catch (err) {
79
+ throw new GeneralError(MessagingAdminService.CLASS_NAME, "getTemplateFailed", {
80
+ templateId,
81
+ locale
82
+ }, err);
83
+ }
79
84
  // If the template is not found for the requested locale, try to get it for the default locale
80
85
  // only if the requested locale is different from the default locale
81
86
  if (Is.empty(templateEntry) && this._defaultLocale !== locale) {
82
87
  try {
83
88
  templateEntry = await this._entityStorageConnector.get(`${templateId}:${this._defaultLocale}`);
84
89
  }
85
- catch { }
90
+ catch (err) {
91
+ throw new GeneralError(MessagingAdminService.CLASS_NAME, "getTemplateFailed", {
92
+ templateId,
93
+ locale: this._defaultLocale
94
+ }, err);
95
+ }
86
96
  }
87
97
  if (Is.empty(templateEntry)) {
88
98
  throw new GeneralError(MessagingAdminService.CLASS_NAME, "getTemplateFailed", {
@@ -1 +1 @@
1
- {"version":3,"file":"messagingAdminService.js","sourceRoot":"","sources":["../../src/messagingAdminService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards, Is } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { IMessagingAdminComponent } from \"@twin.org/messaging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { TemplateEntry } from \"./entities/templateEntry.js\";\nimport type { IMessagingAdminServiceConstructorOptions } from \"./models/IMessagingAdminServiceConstructorOptions.js\";\n\n/**\n * Service for managing message templates stored via entity storage.\n */\nexport class MessagingAdminService implements IMessagingAdminComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MessagingAdminService>();\n\n\t/**\n\t * Default locale for the messaging service.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOCALE: string = \"en\";\n\n\t/**\n\t * Entity storage connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<TemplateEntry>;\n\n\t/**\n\t * The default locale to use for the messaging service.\n\t * @internal\n\t */\n\tprivate readonly _defaultLocale: string;\n\n\t/**\n\t * Create a new instance of MessagingAdminService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMessagingAdminServiceConstructorOptions) {\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.templateEntryStorageConnectorType ?? \"template-entry\"\n\t\t);\n\n\t\tthis._defaultLocale = options?.config?.defaultLocale ?? MessagingAdminService._DEFAULT_LOCALE;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MessagingAdminService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create or update a template.\n\t * @param templateId The id of the template.\n\t * @param locale The locale of the template.\n\t * @param title The title of the template.\n\t * @param content The content of the template.\n\t * @returns A promise that resolves when the template has been stored.\n\t */\n\tpublic async setTemplate(\n\t\ttemplateId: string,\n\t\tlocale: string,\n\t\ttitle: string,\n\t\tcontent: string\n\t): Promise<void> {\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(locale), locale);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(title), title);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(content), content);\n\n\t\tconst templateEntry = new TemplateEntry();\n\t\ttemplateEntry.id = `${templateId}:${locale}`;\n\t\ttemplateEntry.dateCreated = new Date(Date.now()).toISOString();\n\t\ttemplateEntry.title = title;\n\t\ttemplateEntry.content = content;\n\n\t\tawait this._entityStorageConnector.set(templateEntry);\n\t}\n\n\t/**\n\t * Get the email template by id and locale.\n\t * @param templateId The id of the email template.\n\t * @param locale The locale of the email template.\n\t * @returns The email template.\n\t */\n\tpublic async getTemplate(\n\t\ttemplateId: string,\n\t\tlocale: string\n\t): Promise<{ title: string; content: string }> {\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(locale), locale);\n\n\t\tlet templateEntry;\n\n\t\ttry {\n\t\t\t// First try to get the template for the requested locale\n\t\t\ttemplateEntry = await this._entityStorageConnector.get(`${templateId}:${locale}`);\n\t\t} catch {}\n\n\t\t// If the template is not found for the requested locale, try to get it for the default locale\n\t\t// only if the requested locale is different from the default locale\n\t\tif (Is.empty(templateEntry) && this._defaultLocale !== locale) {\n\t\t\ttry {\n\t\t\t\ttemplateEntry = await this._entityStorageConnector.get(\n\t\t\t\t\t`${templateId}:${this._defaultLocale}`\n\t\t\t\t);\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (Is.empty(templateEntry)) {\n\t\t\tthrow new GeneralError(MessagingAdminService.CLASS_NAME, \"getTemplateFailed\", {\n\t\t\t\ttemplateId,\n\t\t\t\tlocale\n\t\t\t});\n\t\t}\n\n\t\treturn templateEntry;\n\t}\n\n\t/**\n\t * Remove a template.\n\t * @param templateId The id of the template.\n\t * @param locale The locale of the template.\n\t * @returns A promise that resolves when the template has been removed.\n\t */\n\tpublic async removeTemplate(templateId: string, locale: string): Promise<void> {\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(locale), locale);\n\n\t\treturn this._entityStorageConnector.remove(`${templateId}:${locale}`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"messagingAdminService.js","sourceRoot":"","sources":["../../src/messagingAdminService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards, Is } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { IMessagingAdminComponent } from \"@twin.org/messaging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { TemplateEntry } from \"./entities/templateEntry.js\";\nimport type { IMessagingAdminServiceConstructorOptions } from \"./models/IMessagingAdminServiceConstructorOptions.js\";\n\n/**\n * Service for managing message templates stored via entity storage.\n */\nexport class MessagingAdminService implements IMessagingAdminComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MessagingAdminService>();\n\n\t/**\n\t * Default locale for the messaging service.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOCALE: string = \"en\";\n\n\t/**\n\t * Entity storage connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<TemplateEntry>;\n\n\t/**\n\t * The default locale to use for the messaging service.\n\t * @internal\n\t */\n\tprivate readonly _defaultLocale: string;\n\n\t/**\n\t * Create a new instance of MessagingAdminService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMessagingAdminServiceConstructorOptions) {\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.templateEntryStorageConnectorType ?? \"template-entry\"\n\t\t);\n\n\t\tthis._defaultLocale = options?.config?.defaultLocale ?? MessagingAdminService._DEFAULT_LOCALE;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MessagingAdminService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create or update a template.\n\t * @param templateId The id of the template.\n\t * @param locale The locale of the template.\n\t * @param title The title of the template.\n\t * @param content The content of the template.\n\t * @returns A promise that resolves when the template has been stored.\n\t */\n\tpublic async setTemplate(\n\t\ttemplateId: string,\n\t\tlocale: string,\n\t\ttitle: string,\n\t\tcontent: string\n\t): Promise<void> {\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(locale), locale);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(title), title);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(content), content);\n\n\t\tconst templateEntry = new TemplateEntry();\n\t\ttemplateEntry.id = `${templateId}:${locale}`;\n\t\ttemplateEntry.dateCreated = new Date(Date.now()).toISOString();\n\t\ttemplateEntry.title = title;\n\t\ttemplateEntry.content = content;\n\n\t\tawait this._entityStorageConnector.set(templateEntry);\n\t}\n\n\t/**\n\t * Get the email template by id and locale.\n\t * @param templateId The id of the email template.\n\t * @param locale The locale of the email template.\n\t * @returns The email template.\n\t */\n\tpublic async getTemplate(\n\t\ttemplateId: string,\n\t\tlocale: string\n\t): Promise<{ title: string; content: string }> {\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(locale), locale);\n\n\t\tlet templateEntry;\n\n\t\ttry {\n\t\t\t// First try to get the template for the requested locale\n\t\t\ttemplateEntry = await this._entityStorageConnector.get(`${templateId}:${locale}`);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMessagingAdminService.CLASS_NAME,\n\t\t\t\t\"getTemplateFailed\",\n\t\t\t\t{\n\t\t\t\t\ttemplateId,\n\t\t\t\t\tlocale\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\n\t\t// If the template is not found for the requested locale, try to get it for the default locale\n\t\t// only if the requested locale is different from the default locale\n\t\tif (Is.empty(templateEntry) && this._defaultLocale !== locale) {\n\t\t\ttry {\n\t\t\t\ttemplateEntry = await this._entityStorageConnector.get(\n\t\t\t\t\t`${templateId}:${this._defaultLocale}`\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tMessagingAdminService.CLASS_NAME,\n\t\t\t\t\t\"getTemplateFailed\",\n\t\t\t\t\t{\n\t\t\t\t\t\ttemplateId,\n\t\t\t\t\t\tlocale: this._defaultLocale\n\t\t\t\t\t},\n\t\t\t\t\terr\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (Is.empty(templateEntry)) {\n\t\t\tthrow new GeneralError(MessagingAdminService.CLASS_NAME, \"getTemplateFailed\", {\n\t\t\t\ttemplateId,\n\t\t\t\tlocale\n\t\t\t});\n\t\t}\n\n\t\treturn templateEntry;\n\t}\n\n\t/**\n\t * Remove a template.\n\t * @param templateId The id of the template.\n\t * @param locale The locale of the template.\n\t * @returns A promise that resolves when the template has been removed.\n\t */\n\tpublic async removeTemplate(templateId: string, locale: string): Promise<void> {\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.stringValue(MessagingAdminService.CLASS_NAME, nameof(locale), locale);\n\n\t\treturn this._entityStorageConnector.remove(`${templateId}:${locale}`);\n\t}\n}\n"]}
@@ -143,9 +143,10 @@ export class MessagingService {
143
143
  let populatedContent = template.content;
144
144
  for (const key in data) {
145
145
  const value = data[key];
146
- const placeholder = `{{${key}}}`;
147
- populatedTitle = populatedTitle.replace(new RegExp(placeholder, "g"), value);
148
- populatedContent = populatedContent.replace(new RegExp(placeholder, "g"), value);
146
+ const escapedKey = key.replace(/[$()*+.?[\\\]^{|}]/g, "\\$&");
147
+ const placeholder = new RegExp(`\\{\\{${escapedKey}\\}\\}`, "g");
148
+ populatedTitle = populatedTitle.replace(placeholder, () => value);
149
+ populatedContent = populatedContent.replace(placeholder, () => value);
149
150
  }
150
151
  return {
151
152
  title: populatedTitle,
@@ -1 +1 @@
1
- {"version":3,"file":"messagingService.js","sourceRoot":"","sources":["../../src/messagingService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ComponentFactory, GeneralError, Guards, Is } from \"@twin.org/core\";\nimport {\n\ttype IMessagingAdminComponent,\n\tMessagingEmailConnectorFactory,\n\tMessagingPushNotificationsConnectorFactory,\n\tMessagingSmsConnectorFactory,\n\ttype IMessagingComponent,\n\ttype IMessagingEmailConnector,\n\ttype IMessagingPushNotificationsConnector,\n\ttype IMessagingSmsConnector\n} from \"@twin.org/messaging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IMessagingServiceConstructorOptions } from \"./models/IMessagingServiceConstructorOptions.js\";\n\n/**\n * Service for dispatching messages via configured email, push notification, and SMS connectors.\n */\nexport class MessagingService implements IMessagingComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MessagingService>();\n\n\t/**\n\t * Emails messaging connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _emailMessagingConnector?: IMessagingEmailConnector;\n\n\t/**\n\t * Push notifications messaging connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _pushNotificationMessagingConnector?: IMessagingPushNotificationsConnector;\n\n\t/**\n\t * SMS messaging connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _smsMessagingConnector?: IMessagingSmsConnector;\n\n\t/**\n\t * The admin component for the messaging.\n\t * @internal\n\t */\n\tprivate readonly _messagingAdminComponent: IMessagingAdminComponent;\n\n\t/**\n\t * Create a new instance of MessagingService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMessagingServiceConstructorOptions) {\n\t\tif (Is.stringValue(options?.messagingEmailConnectorType)) {\n\t\t\tthis._emailMessagingConnector = MessagingEmailConnectorFactory.get(\n\t\t\t\toptions.messagingEmailConnectorType\n\t\t\t);\n\t\t}\n\n\t\tif (Is.stringValue(options?.messagingPushNotificationConnectorType)) {\n\t\t\tthis._pushNotificationMessagingConnector = MessagingPushNotificationsConnectorFactory.get(\n\t\t\t\toptions.messagingPushNotificationConnectorType\n\t\t\t);\n\t\t}\n\n\t\tif (Is.stringValue(options?.messagingSmsConnectorType)) {\n\t\t\tthis._smsMessagingConnector = MessagingSmsConnectorFactory.get(\n\t\t\t\toptions.messagingSmsConnectorType\n\t\t\t);\n\t\t}\n\n\t\tthis._messagingAdminComponent = ComponentFactory.get(\n\t\t\toptions?.messagingAdminComponentType ?? \"messaging-admin\"\n\t\t);\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MessagingService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Send a custom email.\n\t * @param sender The sender email address.\n\t * @param recipients An array of recipients email addresses.\n\t * @param templateId The id of the email template.\n\t * @param data The data to populate the email template.\n\t * @param locale The locale of the email template.\n\t * @returns True if the email was sent successfully.\n\t */\n\tpublic async sendCustomEmail(\n\t\tsender: string,\n\t\trecipients: string[],\n\t\ttemplateId: string,\n\t\tdata: { [key: string]: string },\n\t\tlocale: string\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._emailMessagingConnector)) {\n\t\t\tthrow new GeneralError(MessagingService.CLASS_NAME, \"notConfiguredEmailMessagingConnector\");\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(sender), sender);\n\t\tGuards.arrayValue(MessagingService.CLASS_NAME, nameof(recipients), recipients);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.object(MessagingService.CLASS_NAME, nameof(data), data);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(locale), locale);\n\n\t\tconst template = await this._messagingAdminComponent.getTemplate(templateId, locale);\n\t\tconst populatedTemplate = this.populateTemplate(template, data);\n\n\t\treturn this._emailMessagingConnector.sendCustomEmail(\n\t\t\tsender,\n\t\t\trecipients,\n\t\t\tpopulatedTemplate.title,\n\t\t\tpopulatedTemplate.content\n\t\t);\n\t}\n\n\t/**\n\t * Registers a device to a specific application in order to send notifications to it.\n\t * @param applicationId The application address.\n\t * @param deviceToken The device token.\n\t * @returns The address assigned to the registered device.\n\t */\n\tpublic async registerDevice(applicationId: string, deviceToken: string): Promise<string> {\n\t\tif (Is.empty(this._pushNotificationMessagingConnector)) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMessagingService.CLASS_NAME,\n\t\t\t\t\"notConfiguredPushNotificationMessagingConnector\"\n\t\t\t);\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(applicationId), applicationId);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(deviceToken), deviceToken);\n\n\t\treturn this._pushNotificationMessagingConnector.registerDevice(applicationId, deviceToken);\n\t}\n\n\t/**\n\t * Send a push notification to a device.\n\t * @param deviceAddress The address of the device.\n\t * @param templateId The id of the push notification template.\n\t * @param data The data to populate the push notification template.\n\t * @param locale The locale of the push notification template.\n\t * @returns True if the notification was sent successfully.\n\t */\n\tpublic async sendSinglePushNotification(\n\t\tdeviceAddress: string,\n\t\ttemplateId: string,\n\t\tdata: { [key: string]: string },\n\t\tlocale: string\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._pushNotificationMessagingConnector)) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMessagingService.CLASS_NAME,\n\t\t\t\t\"notConfiguredPushNotificationMessagingConnector\"\n\t\t\t);\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(deviceAddress), deviceAddress);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.object(MessagingService.CLASS_NAME, nameof(data), data);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(locale), locale);\n\n\t\tconst template = await this._messagingAdminComponent.getTemplate(templateId, locale);\n\t\tconst populatedTemplate = this.populateTemplate(template, data);\n\n\t\treturn this._pushNotificationMessagingConnector.sendSinglePushNotification(\n\t\t\tdeviceAddress,\n\t\t\tpopulatedTemplate.title,\n\t\t\tpopulatedTemplate.content\n\t\t);\n\t}\n\n\t/**\n\t * Send a SMS message to a phone number.\n\t * @param phoneNumber The recipient phone number.\n\t * @param templateId The id of the SMS template.\n\t * @param data The data to populate the SMS template.\n\t * @param locale The locale of the SMS template.\n\t * @returns True if the SMS was sent successfully.\n\t */\n\tpublic async sendSMS(\n\t\tphoneNumber: string,\n\t\ttemplateId: string,\n\t\tdata: { [key: string]: string },\n\t\tlocale: string\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._smsMessagingConnector)) {\n\t\t\tthrow new GeneralError(MessagingService.CLASS_NAME, \"notConfiguredSmsMessagingConnector\");\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(phoneNumber), phoneNumber);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.object(MessagingService.CLASS_NAME, nameof(data), data);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(locale), locale);\n\n\t\tconst template = await this._messagingAdminComponent.getTemplate(templateId, locale);\n\t\tconst populatedTemplate = this.populateTemplate(template, data);\n\n\t\treturn this._smsMessagingConnector.sendSMS(phoneNumber, populatedTemplate.content);\n\t}\n\n\t/**\n\t * Populates a template by replacing placeholders with the provided data values.\n\t * @param template The template.\n\t * @param template.title The title of the template.\n\t * @param template.content The content of the template.\n\t * @param data The data to populate the template.\n\t * @returns The template with all placeholders replaced.\n\t * @internal\n\t */\n\tprivate populateTemplate(\n\t\ttemplate: { title: string; content: string },\n\t\tdata: { [key: string]: string }\n\t): { title: string; content: string } {\n\t\tlet populatedTitle = template.title;\n\t\tlet populatedContent = template.content;\n\n\t\tfor (const key in data) {\n\t\t\tconst value = data[key];\n\t\t\tconst placeholder = `{{${key}}}`;\n\t\t\tpopulatedTitle = populatedTitle.replace(new RegExp(placeholder, \"g\"), value);\n\t\t\tpopulatedContent = populatedContent.replace(new RegExp(placeholder, \"g\"), value);\n\t\t}\n\n\t\treturn {\n\t\t\ttitle: populatedTitle,\n\t\t\tcontent: populatedContent\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"messagingService.js","sourceRoot":"","sources":["../../src/messagingService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ComponentFactory, GeneralError, Guards, Is } from \"@twin.org/core\";\nimport {\n\ttype IMessagingAdminComponent,\n\tMessagingEmailConnectorFactory,\n\tMessagingPushNotificationsConnectorFactory,\n\tMessagingSmsConnectorFactory,\n\ttype IMessagingComponent,\n\ttype IMessagingEmailConnector,\n\ttype IMessagingPushNotificationsConnector,\n\ttype IMessagingSmsConnector\n} from \"@twin.org/messaging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IMessagingServiceConstructorOptions } from \"./models/IMessagingServiceConstructorOptions.js\";\n\n/**\n * Service for dispatching messages via configured email, push notification, and SMS connectors.\n */\nexport class MessagingService implements IMessagingComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MessagingService>();\n\n\t/**\n\t * Emails messaging connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _emailMessagingConnector?: IMessagingEmailConnector;\n\n\t/**\n\t * Push notifications messaging connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _pushNotificationMessagingConnector?: IMessagingPushNotificationsConnector;\n\n\t/**\n\t * SMS messaging connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _smsMessagingConnector?: IMessagingSmsConnector;\n\n\t/**\n\t * The admin component for the messaging.\n\t * @internal\n\t */\n\tprivate readonly _messagingAdminComponent: IMessagingAdminComponent;\n\n\t/**\n\t * Create a new instance of MessagingService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMessagingServiceConstructorOptions) {\n\t\tif (Is.stringValue(options?.messagingEmailConnectorType)) {\n\t\t\tthis._emailMessagingConnector = MessagingEmailConnectorFactory.get(\n\t\t\t\toptions.messagingEmailConnectorType\n\t\t\t);\n\t\t}\n\n\t\tif (Is.stringValue(options?.messagingPushNotificationConnectorType)) {\n\t\t\tthis._pushNotificationMessagingConnector = MessagingPushNotificationsConnectorFactory.get(\n\t\t\t\toptions.messagingPushNotificationConnectorType\n\t\t\t);\n\t\t}\n\n\t\tif (Is.stringValue(options?.messagingSmsConnectorType)) {\n\t\t\tthis._smsMessagingConnector = MessagingSmsConnectorFactory.get(\n\t\t\t\toptions.messagingSmsConnectorType\n\t\t\t);\n\t\t}\n\n\t\tthis._messagingAdminComponent = ComponentFactory.get(\n\t\t\toptions?.messagingAdminComponentType ?? \"messaging-admin\"\n\t\t);\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn MessagingService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Send a custom email.\n\t * @param sender The sender email address.\n\t * @param recipients An array of recipients email addresses.\n\t * @param templateId The id of the email template.\n\t * @param data The data to populate the email template.\n\t * @param locale The locale of the email template.\n\t * @returns True if the email was sent successfully.\n\t */\n\tpublic async sendCustomEmail(\n\t\tsender: string,\n\t\trecipients: string[],\n\t\ttemplateId: string,\n\t\tdata: { [key: string]: string },\n\t\tlocale: string\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._emailMessagingConnector)) {\n\t\t\tthrow new GeneralError(MessagingService.CLASS_NAME, \"notConfiguredEmailMessagingConnector\");\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(sender), sender);\n\t\tGuards.arrayValue(MessagingService.CLASS_NAME, nameof(recipients), recipients);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.object(MessagingService.CLASS_NAME, nameof(data), data);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(locale), locale);\n\n\t\tconst template = await this._messagingAdminComponent.getTemplate(templateId, locale);\n\t\tconst populatedTemplate = this.populateTemplate(template, data);\n\n\t\treturn this._emailMessagingConnector.sendCustomEmail(\n\t\t\tsender,\n\t\t\trecipients,\n\t\t\tpopulatedTemplate.title,\n\t\t\tpopulatedTemplate.content\n\t\t);\n\t}\n\n\t/**\n\t * Registers a device to a specific application in order to send notifications to it.\n\t * @param applicationId The application address.\n\t * @param deviceToken The device token.\n\t * @returns The address assigned to the registered device.\n\t */\n\tpublic async registerDevice(applicationId: string, deviceToken: string): Promise<string> {\n\t\tif (Is.empty(this._pushNotificationMessagingConnector)) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMessagingService.CLASS_NAME,\n\t\t\t\t\"notConfiguredPushNotificationMessagingConnector\"\n\t\t\t);\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(applicationId), applicationId);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(deviceToken), deviceToken);\n\n\t\treturn this._pushNotificationMessagingConnector.registerDevice(applicationId, deviceToken);\n\t}\n\n\t/**\n\t * Send a push notification to a device.\n\t * @param deviceAddress The address of the device.\n\t * @param templateId The id of the push notification template.\n\t * @param data The data to populate the push notification template.\n\t * @param locale The locale of the push notification template.\n\t * @returns True if the notification was sent successfully.\n\t */\n\tpublic async sendSinglePushNotification(\n\t\tdeviceAddress: string,\n\t\ttemplateId: string,\n\t\tdata: { [key: string]: string },\n\t\tlocale: string\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._pushNotificationMessagingConnector)) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tMessagingService.CLASS_NAME,\n\t\t\t\t\"notConfiguredPushNotificationMessagingConnector\"\n\t\t\t);\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(deviceAddress), deviceAddress);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.object(MessagingService.CLASS_NAME, nameof(data), data);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(locale), locale);\n\n\t\tconst template = await this._messagingAdminComponent.getTemplate(templateId, locale);\n\t\tconst populatedTemplate = this.populateTemplate(template, data);\n\n\t\treturn this._pushNotificationMessagingConnector.sendSinglePushNotification(\n\t\t\tdeviceAddress,\n\t\t\tpopulatedTemplate.title,\n\t\t\tpopulatedTemplate.content\n\t\t);\n\t}\n\n\t/**\n\t * Send a SMS message to a phone number.\n\t * @param phoneNumber The recipient phone number.\n\t * @param templateId The id of the SMS template.\n\t * @param data The data to populate the SMS template.\n\t * @param locale The locale of the SMS template.\n\t * @returns True if the SMS was sent successfully.\n\t */\n\tpublic async sendSMS(\n\t\tphoneNumber: string,\n\t\ttemplateId: string,\n\t\tdata: { [key: string]: string },\n\t\tlocale: string\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._smsMessagingConnector)) {\n\t\t\tthrow new GeneralError(MessagingService.CLASS_NAME, \"notConfiguredSmsMessagingConnector\");\n\t\t}\n\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(phoneNumber), phoneNumber);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(templateId), templateId);\n\t\tGuards.object(MessagingService.CLASS_NAME, nameof(data), data);\n\t\tGuards.stringValue(MessagingService.CLASS_NAME, nameof(locale), locale);\n\n\t\tconst template = await this._messagingAdminComponent.getTemplate(templateId, locale);\n\t\tconst populatedTemplate = this.populateTemplate(template, data);\n\n\t\treturn this._smsMessagingConnector.sendSMS(phoneNumber, populatedTemplate.content);\n\t}\n\n\t/**\n\t * Populates a template by replacing placeholders with the provided data values.\n\t * @param template The template.\n\t * @param template.title The title of the template.\n\t * @param template.content The content of the template.\n\t * @param data The data to populate the template.\n\t * @returns The template with all placeholders replaced.\n\t * @internal\n\t */\n\tprivate populateTemplate(\n\t\ttemplate: { title: string; content: string },\n\t\tdata: { [key: string]: string }\n\t): { title: string; content: string } {\n\t\tlet populatedTitle = template.title;\n\t\tlet populatedContent = template.content;\n\n\t\tfor (const key in data) {\n\t\t\tconst value = data[key];\n\t\t\tconst escapedKey = key.replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\");\n\t\t\tconst placeholder = new RegExp(`\\\\{\\\\{${escapedKey}\\\\}\\\\}`, \"g\");\n\t\t\tpopulatedTitle = populatedTitle.replace(placeholder, () => value);\n\t\t\tpopulatedContent = populatedContent.replace(placeholder, () => value);\n\t\t}\n\n\t\treturn {\n\t\t\ttitle: populatedTitle,\n\t\t\tcontent: populatedContent\n\t\t};\n\t}\n}\n"]}
package/docs/changelog.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.3-next.1](https://github.com/iotaledger/twin-messaging/compare/messaging-service-v0.9.3-next.0...messaging-service-v0.9.3-next.1) (2026-09-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * add context id features ([#24](https://github.com/iotaledger/twin-messaging/issues/24)) ([42b94e8](https://github.com/iotaledger/twin-messaging/commit/42b94e8be32c86db6afc5ccf5e9ff2c93d15d05d))
9
+ * add email ingestion ([#56](https://github.com/iotaledger/twin-messaging/issues/56)) ([390806d](https://github.com/iotaledger/twin-messaging/commit/390806d2690843f44de0b6a8a5e5de616b16c278))
10
+ * add initial templates config ([66b40eb](https://github.com/iotaledger/twin-messaging/commit/66b40eb57a478d9f79de540eb847a4fb37894235))
11
+ * add messaging admin component ([cbaaca3](https://github.com/iotaledger/twin-messaging/commit/cbaaca34db6a9f5c51438c201535b4b43a1aea1f))
12
+ * add validate-locales ([5eeff9b](https://github.com/iotaledger/twin-messaging/commit/5eeff9b9bf6cecf93b249b56c4bb74b4c5bf86c9))
13
+ * eslint migration to flat config ([faa02ec](https://github.com/iotaledger/twin-messaging/commit/faa02ec0ef450db88b08e938415e40cf13625d15))
14
+ * linting and dependency update ([12474b0](https://github.com/iotaledger/twin-messaging/commit/12474b0d24d280ba7b6c1525c7e23ea01a354024))
15
+ * Messaging services ([#3](https://github.com/iotaledger/twin-messaging/issues/3)) ([32571b5](https://github.com/iotaledger/twin-messaging/commit/32571b5abf5d3fc3b168074c23507e926c5d00b0))
16
+ * remove unused namespace ([2201898](https://github.com/iotaledger/twin-messaging/commit/22018988b84e4b3e76da1a1e16f29bfde7e6f0e6))
17
+ * typescript 6 update ([0aabd3e](https://github.com/iotaledger/twin-messaging/commit/0aabd3e05fbdf1cbc5df34cf24c461a75abc9d7e))
18
+ * update dependencies ([eda66c5](https://github.com/iotaledger/twin-messaging/commit/eda66c5c670f36f1ba86a8f768e4781b629c88ff))
19
+ * update dependencies ([53dcde6](https://github.com/iotaledger/twin-messaging/commit/53dcde60d6efaee5957296f6d097cb97c0fb2f9d))
20
+ * update framework core ([fc476b4](https://github.com/iotaledger/twin-messaging/commit/fc476b48e6075de0ae871e3cec1fa179aa04b5eb))
21
+ * use shared store mechanism ([#8](https://github.com/iotaledger/twin-messaging/issues/8)) ([9ad65c2](https://github.com/iotaledger/twin-messaging/commit/9ad65c239ba77bb75604a1f6e51b975357f3228d))
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * Adding format to the MessagingEntries ts number ([8b99d4f](https://github.com/iotaledger/twin-messaging/commit/8b99d4f01c4f2b08da8d2affc1b9554fcb0d3690))
27
+ * use async getStore in tests ([bd00e17](https://github.com/iotaledger/twin-messaging/commit/bd00e17218c1954bbb3a740e5628756764fa2598))
28
+ * use async getStore in tests ([50057a6](https://github.com/iotaledger/twin-messaging/commit/50057a6ae962623aaa2ab778ea29c08b6f885452))
29
+
30
+
31
+ ### Dependencies
32
+
33
+ * The following workspace dependencies were updated
34
+ * dependencies
35
+ * @twin.org/messaging-models bumped from 0.9.3-next.0 to 0.9.3-next.1
36
+
3
37
  ## [0.9.2](https://github.com/iotaledger/twin-messaging/compare/messaging-service-v0.9.2...messaging-service-v0.9.2) (2026-08-24)
4
38
 
5
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/messaging-service",
3
- "version": "0.9.2",
3
+ "version": "0.9.3-next.1",
4
4
  "description": "Implements the messaging service layer that orchestrates delivery workflows across connectors.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,12 +14,12 @@
14
14
  "node": ">=24.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/core": "^0.9.2",
18
- "@twin.org/entity": "^0.9.2",
19
- "@twin.org/entity-storage-models": "^0.9.2",
20
- "@twin.org/logging-models": "^0.9.2",
21
- "@twin.org/messaging-models": "^0.9.2",
22
- "@twin.org/nameof": "^0.9.2"
17
+ "@twin.org/core": "next",
18
+ "@twin.org/entity": "next",
19
+ "@twin.org/entity-storage-models": "next",
20
+ "@twin.org/logging-models": "next",
21
+ "@twin.org/messaging-models": "0.9.3-next.1",
22
+ "@twin.org/nameof": "next"
23
23
  },
24
24
  "main": "./dist/es/index.js",
25
25
  "types": "./dist/types/index.d.ts",