diff --git a/src/Sinch/Conversation/SinchConversationClient.cs b/src/Sinch/Conversation/SinchConversationClient.cs
index 11d622db..4a81ea1d 100644
--- a/src/Sinch/Conversation/SinchConversationClient.cs
+++ b/src/Sinch/Conversation/SinchConversationClient.cs
@@ -6,6 +6,7 @@
using Sinch.Conversation.Events;
using Sinch.Conversation.Messages;
using Sinch.Conversation.Transcoding;
+using Sinch.Conversation.TemplatesV2;
using Sinch.Conversation.Webhooks;
using Sinch.Core;
using Sinch.Logger;
@@ -43,28 +44,34 @@ public interface ISinchConversation
///
ISinchConversationCapabilities Capabilities { get; }
+
+ ///
+ ISinchConversationTemplatesV2 TemplatesV2 { get; }
}
///
internal class SinchConversationClient : ISinchConversation
{
- internal SinchConversationClient(string projectId, Uri baseAddress, LoggerFactory loggerFactory, IHttp http)
+ internal SinchConversationClient(string projectId, Uri conversationBaseAddress, Uri templatesBaseAddress
+ , LoggerFactory loggerFactory, IHttp http)
{
- Messages = new Messages.Messages(projectId, baseAddress,
+ Messages = new Messages.Messages(projectId, conversationBaseAddress,
loggerFactory?.Create(),
http);
- Apps = new Apps.Apps(projectId, baseAddress, loggerFactory?.Create(), http);
- Contacts = new Contacts.Contacts(projectId, baseAddress,
+ Apps = new Apps.Apps(projectId, conversationBaseAddress, loggerFactory?.Create(), http);
+ Contacts = new Contacts.Contacts(projectId, conversationBaseAddress,
loggerFactory?.Create(), http);
- Conversations = new ConversationsClient(projectId, baseAddress,
+ Conversations = new ConversationsClient(projectId, conversationBaseAddress,
loggerFactory?.Create(), http);
- Webhooks = new Webhooks.Webhooks(projectId, baseAddress,
+ Webhooks = new Webhooks.Webhooks(projectId, conversationBaseAddress,
loggerFactory?.Create(), http);
- Events = new Events.Events(projectId, baseAddress, loggerFactory?.Create(), http);
- Transcoding = new Transcoding.Transcoding(projectId, baseAddress,
+ Events = new Events.Events(projectId, conversationBaseAddress, loggerFactory?.Create(), http);
+ Transcoding = new Transcoding.Transcoding(projectId, conversationBaseAddress,
loggerFactory?.Create(), http);
- Capabilities = new Capabilities(projectId, baseAddress,
+ Capabilities = new Capabilities(projectId, conversationBaseAddress,
loggerFactory?.Create(), http);
+ TemplatesV2 = new TemplatesV2.TemplatesV2(projectId, templatesBaseAddress,
+ loggerFactory?.Create(), http);
}
///
@@ -90,5 +97,8 @@ internal SinchConversationClient(string projectId, Uri baseAddress, LoggerFactor
///
public ISinchConversationCapabilities Capabilities { get; }
+
+ ///
+ public ISinchConversationTemplatesV2 TemplatesV2 { get; }
}
}
diff --git a/src/Sinch/Conversation/TemplatesV2/ChannelTemplateOverride.cs b/src/Sinch/Conversation/TemplatesV2/ChannelTemplateOverride.cs
new file mode 100644
index 00000000..641fd1c8
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/ChannelTemplateOverride.cs
@@ -0,0 +1,41 @@
+using System.Text;
+using System.Text.Json.Serialization;
+using Sinch.Conversation.Messages.Message;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ ///
+ /// Optional field to override the omnichannel template by referring to a channel-specific template.
+ ///
+ public sealed class ChannelTemplateOverride
+ {
+ ///
+ /// Gets or Sets Whatsapp
+ ///
+ [JsonPropertyName("WHATSAPP")]
+ public OverrideTemplateReference WhatsApp { get; set; }
+
+
+ ///
+ /// Gets or Sets Kakaotalk
+ ///
+ [JsonPropertyName("KAKAOTALK")]
+ public OverrideTemplateReference KakaoTalk { get; set; }
+
+
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("class ChannelTemplateOverride {\n");
+ sb.Append(" WHATSAPP: ").Append(WhatsApp).Append("\n");
+ sb.Append(" KAKOTALK: ").Append(KakaoTalk).Append("\n");
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+
+ }
+}
diff --git a/src/Sinch/Conversation/TemplatesV2/CreateTemplateRequest.cs b/src/Sinch/Conversation/TemplatesV2/CreateTemplateRequest.cs
new file mode 100644
index 00000000..7151e90a
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/CreateTemplateRequest.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ public class CreateTemplateRequest
+ {
+ ///
+ /// The default translation to use if translation not specified. Specified as a BCP-47 `language_code` and the `language_code` must exist in the translations list.
+ ///
+#if NET7_0_OR_GREATER
+ public required string DefaultTranslation { get; set; }
+#else
+ public string DefaultTranslation { get; set; }
+#endif
+
+ ///
+ /// Gets or Sets Translations
+ ///
+#if NET7_0_OR_GREATER
+ public required List Translations { get; set; }
+#else
+ public List Translations { get; set; }
+#endif
+
+
+ ///
+ /// The description of the template.
+ ///
+ public string Description { get; set; }
+
+
+ ///
+ /// The version of the template. While creating a template, this will be defaulted to 1. When updating a template, you must supply the latest version of the template in order for the update to be successful.
+ ///
+ public int Version { get; set; }
+
+
+ ///
+ /// Timestamp when the template was created.
+ ///
+ public DateTime CreateTime { get; set; }
+
+
+ ///
+ /// Timestamp when the template was updated.
+ ///
+ public DateTime UpdateTime { get; set; }
+
+
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("class V2TemplateResponse {\n");
+ sb.Append(" Description: ").Append(Description).Append("\n");
+ sb.Append(" Version: ").Append(Version).Append("\n");
+ sb.Append(" DefaultTranslation: ").Append(DefaultTranslation).Append("\n");
+ sb.Append(" Translations: ").Append(Translations).Append("\n");
+ sb.Append(" CreateTime: ").Append(CreateTime).Append("\n");
+ sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n");
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/Sinch/Conversation/TemplatesV2/OverrideTemplateReference.cs b/src/Sinch/Conversation/TemplatesV2/OverrideTemplateReference.cs
new file mode 100644
index 00000000..11e24aec
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/OverrideTemplateReference.cs
@@ -0,0 +1,32 @@
+using System.Text;
+using Sinch.Conversation.Messages.Message;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ public class OverrideTemplateReference
+ {
+ ///
+ /// The referenced template can be an omnichannel template stored in Conversation API Template Store as AppMessage or it can reference external channel-specific template such as WhatsApp Business Template.
+ ///
+ public TemplateReference TemplateReference { get; set; }
+
+ ///
+ /// Gets or Sets ParameterMappings
+ ///
+ public TemplateReferenceParameterMappings ParameterMappings { get; set; }
+
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("class TemplateReference {\n");
+ sb.Append(" TemplateReference: ").Append(TemplateReference).Append("\n");
+ sb.Append(" ParameterMappings: ").Append(ParameterMappings).Append("\n");
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/Sinch/Conversation/TemplatesV2/Template.cs b/src/Sinch/Conversation/TemplatesV2/Template.cs
new file mode 100644
index 00000000..c2d85a19
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/Template.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ public class Template
+ {
+ ///
+ /// The id of the template. Specify this yourself during creation. Otherwise, we will generate an ID for you. This must
+ /// be unique for a given project.
+ ///
+ public string Id { get; set; }
+
+
+ ///
+ /// The description of the template.
+ ///
+ public string Description { get; set; }
+
+
+ ///
+ /// The version of the template. While creating a template, this will be defaulted to 1. When updating a template, you
+ /// must supply the latest version of the template in order for the update to be successful.
+ ///
+ public int Version { get; set; }
+
+
+ ///
+ /// The default translation to use if translation not specified. Specified as a BCP-47 `language_code` and
+ /// the `language_code` must exist in the translations list.
+ ///
+ public string DefaultTranslation { get; set; }
+
+
+ ///
+ /// Gets or Sets Translations
+ ///
+ public List Translations { get; set; }
+
+
+ ///
+ /// Timestamp when the template was created.
+ ///
+ public DateTime CreateTime { get; set; }
+
+
+ ///
+ /// Timestamp when the template was updated.
+ ///
+ public DateTime UpdateTime { get; set; }
+
+
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("class V2TemplateResponse {\n");
+ sb.Append(" Id: ").Append(Id).Append("\n");
+ sb.Append(" Description: ").Append(Description).Append("\n");
+ sb.Append(" Version: ").Append(Version).Append("\n");
+ sb.Append(" DefaultTranslation: ").Append(DefaultTranslation).Append("\n");
+ sb.Append(" Translations: ").Append(Translations).Append("\n");
+ sb.Append(" CreateTime: ").Append(CreateTime).Append("\n");
+ sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n");
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/Sinch/Conversation/TemplatesV2/TemplateReferenceParameterMappings.cs b/src/Sinch/Conversation/TemplatesV2/TemplateReferenceParameterMappings.cs
new file mode 100644
index 00000000..b5fa6841
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/TemplateReferenceParameterMappings.cs
@@ -0,0 +1,31 @@
+using System.Text;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ ///
+ /// A mapping between omni-template variables and the channel specific parameters.
+ ///
+ public sealed class TemplateReferenceParameterMappings
+ {
+ ///
+ /// The mapping between the omni-template variable and the channel specific parameter.
+ ///
+ public string Name { get; set; }
+
+
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("class TemplateReferenceParameterMappings {\n");
+ sb.Append(" Name: ").Append(Name).Append("\n");
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+
+ }
+
+}
diff --git a/src/Sinch/Conversation/TemplatesV2/TemplateTranslation.cs b/src/Sinch/Conversation/TemplatesV2/TemplateTranslation.cs
new file mode 100644
index 00000000..f7a1f0e5
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/TemplateTranslation.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json.Serialization;
+using Sinch.Conversation.Messages.Message;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ public class TemplateTranslation
+ {
+ // Thank you System.Text.Json -_-
+ [JsonConstructor]
+ [Obsolete("Needed for System.Text.Json", true)]
+ public TemplateTranslation()
+ {
+ }
+
+ public TemplateTranslation(ChoiceMessage choiceMessage)
+ {
+ ChoiceMessage = choiceMessage;
+ }
+
+ public TemplateTranslation(LocationMessage locationMessage)
+ {
+ LocationMessage = locationMessage;
+ }
+
+ public TemplateTranslation(MediaMessage mediaMessage)
+ {
+ MediaMessage = mediaMessage;
+ }
+
+ public TemplateTranslation(TemplateMessage templateMessage)
+ {
+ TemplateMessage = templateMessage;
+ }
+
+ public TemplateTranslation(ListMessage listMessage)
+ {
+ ListMessage = listMessage;
+ }
+
+ public TemplateTranslation(TextMessage textMessage)
+ {
+ TextMessage = textMessage;
+ }
+
+ public TemplateTranslation(CardMessage cardMessage)
+ {
+ CardMessage = cardMessage;
+ }
+
+ public TemplateTranslation(CarouselMessage carouselMessage)
+ {
+ CarouselMessage = carouselMessage;
+ }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public TextMessage TextMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public CardMessage CardMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public CarouselMessage CarouselMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public ChoiceMessage ChoiceMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public LocationMessage LocationMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public MediaMessage MediaMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public TemplateMessage TemplateMessage { get; private set; }
+
+ [JsonInclude]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public ListMessage ListMessage { get; private set; }
+
+ ///
+ /// The BCP-47 language code, such as `en-US` or `sr-Latn`. For more information, see http://www.unicode.org/reports/tr35/#Unicode_locale_identifier.
+ ///
+#if NET7_0_OR_GREATER
+ public required string LanguageCode { get; set; }
+#else
+ public string LanguageCode { get; set; }
+#endif
+
+ ///
+ /// The version of the translation.
+ ///
+ public string Version { get; set; }
+
+
+ ///
+ /// Gets or Sets ChannelTemplateOverrides
+ ///
+ public ChannelTemplateOverride ChannelTemplateOverrides { get; set; }
+
+
+ ///
+ /// List of expected variables. Can be used for request validation.
+ ///
+ public List Variables { get; set; }
+
+
+ ///
+ /// Timestamp when the translation was created.
+ ///
+ public DateTime CreateTime { get; set; }
+
+
+ ///
+ /// Timestamp of when the translation was updated.
+ ///
+ public DateTime UpdateTime { get; set; }
+
+
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("class V2TemplateTranslation {\n");
+ sb.Append(" LanguageCode: ").Append(LanguageCode).Append("\n");
+ sb.Append(" Version: ").Append(Version).Append("\n");
+ sb.Append(" TextMessage: ").Append(TextMessage).Append("\n");
+ sb.Append(" CardMessage: ").Append(CardMessage).Append("\n");
+ sb.Append(" CarouselMessage: ").Append(CarouselMessage).Append("\n");
+ sb.Append(" ChoiceMessage: ").Append(ChoiceMessage).Append("\n");
+ sb.Append(" LocationMessage: ").Append(LocationMessage).Append("\n");
+ sb.Append(" MediaMessage: ").Append(MediaMessage).Append("\n");
+ sb.Append(" TemplateMessage: ").Append(TemplateMessage).Append("\n");
+ sb.Append(" ListMessage: ").Append(ListMessage).Append("\n");
+ sb.Append(" ChannelTemplateOverrides: ").Append(ChannelTemplateOverrides).Append("\n");
+ sb.Append(" Variables: ").Append(Variables).Append("\n");
+ sb.Append(" CreateTime: ").Append(CreateTime).Append("\n");
+ sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n");
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/Sinch/Conversation/TemplatesV2/TemplatesV2.cs b/src/Sinch/Conversation/TemplatesV2/TemplatesV2.cs
new file mode 100644
index 00000000..756a9878
--- /dev/null
+++ b/src/Sinch/Conversation/TemplatesV2/TemplatesV2.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web;
+using Sinch.Core;
+using Sinch.Logger;
+
+namespace Sinch.Conversation.TemplatesV2
+{
+ ///
+ /// The Template Management API offers a way to manage templates that can be used together with the Conversation API.
+ /// Note that you may also use the Message Composer tool on the [Sinch Customer
+ /// Dashboard](https://dashboard.sinch.com/convapi/message-composer) to [manage
+ /// templates](https://community.sinch.com/t5/Conversation-API/How-do-I-use-Message-Composer-to-create-omni-channel-message/ta-p/9890).
+ /// One can view a template as a pre-defined message that can optionally contain some parameters to facilitate some
+ /// customization of the pre-defined message. This feature can, for instance, be used to construct a generic customer
+ /// welcome message where the customer's name can be injected via a parameter. It's also possible to provide
+ /// translations to different languages when creating a template to make it possible to reuse one template for
+ /// different languages.
+ ///
+ public interface ISinchConversationTemplatesV2
+ {
+ ///
+ /// Get a template
+ ///
+ ///
+ ///
+ ///
+ Task Get(string templateId, CancellationToken cancellationToken = default);
+
+ ///
+ /// List all templates
+ ///
+ ///
+ ///
+ Task> List(CancellationToken cancellationToken = default);
+
+ ///
+ /// Creates a template
+ ///
+ /// Template to create
+ ///
+ ///
+ Task Create(CreateTemplateRequest template, CancellationToken cancellationToken = default);
+
+ ///
+ ///
+ ///
+ ///
+ /// Optional. The translation's language code.
+ /// Optional. The translation's version.
+ ///
+ ///
+ Task> ListTranslations(string templateId, string languageCode,
+ string translationVersion,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Update the template
+ ///
+ ///
+ ///
+ ///
+ Task Update(UpdateTemplateRequest template, CancellationToken cancellationToken = default);
+
+ ///
+ /// Deletes a template
+ ///
+ ///
+ ///
+ ///
+ Task Delete(string templateId, CancellationToken cancellationToken = default);
+ }
+
+ internal class TemplatesV2 : ISinchConversationTemplatesV2
+ {
+ private readonly Uri _baseAddress;
+ private readonly IHttp _http;
+ private readonly ILoggerAdapter _logger;
+ private readonly string _projectId;
+
+ public TemplatesV2(string projectId, Uri baseAddress, ILoggerAdapter logger,
+ IHttp http)
+ {
+ _projectId = projectId;
+ _baseAddress = baseAddress;
+ _http = http;
+ _logger = logger;
+ }
+
+ ///
+ public Task Get(string templateId, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(templateId))
+ {
+ throw new ArgumentNullException(nameof(templateId));
+ }
+
+ var uri = new Uri(_baseAddress, $"v2/projects/{_projectId}/templates/{templateId}");
+
+ _logger?.LogDebug($"Getting a template with {templateId}...", templateId);
+ return _http.Send(uri, HttpMethod.Get, cancellationToken: cancellationToken);
+ }
+
+ public async Task> List(CancellationToken cancellationToken = default)
+ {
+ var uri = new Uri(_baseAddress, $"v2/projects/{_projectId}/templates");
+
+ _logger?.LogDebug("Listing all template of {projectId}", _projectId);
+ var response =
+ await _http.Send(uri, HttpMethod.Get, cancellationToken: cancellationToken);
+ return response.Templates;
+ }
+
+ private class ListTemplatesResponse
+ {
+ public List Templates { get; set; }
+ }
+
+ ///
+ public Task Create(CreateTemplateRequest template, CancellationToken cancellationToken = default)
+ {
+ var uri = new Uri(_baseAddress, $"v2/projects/{_projectId}/templates");
+
+ _logger?.LogDebug("Creating a template in {projectId}", _projectId);
+ return _http.Send(uri, HttpMethod.Post, template, cancellationToken: cancellationToken);
+ }
+
+ ///
+ public async Task> ListTranslations(string templateId, string languageCode,
+ string translationVersion, CancellationToken cancellationToken = default)
+ {
+ var uri = new Uri(_baseAddress, $"v2/projects/{_projectId}/templates/{templateId}/translations");
+
+ var builder = new UriBuilder(uri);
+ var parameters = HttpUtility.ParseQueryString(string.Empty);
+
+ if (!string.IsNullOrEmpty(languageCode))
+ {
+ parameters["language_code"] = languageCode;
+ }
+
+ if (!string.IsNullOrEmpty(translationVersion))
+ {
+ parameters["translation_version"] = translationVersion;
+ }
+
+ builder.Query = parameters.ToString()!;
+ _logger?.LogDebug("Listing a translations for {templateId}", _projectId);
+ var response =
+ await _http.Send(builder.Uri, HttpMethod.Get,
+ cancellationToken: cancellationToken);
+ return response.Translations;
+ }
+
+ public Task Update(UpdateTemplateRequest template, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(template.Id))
+ {
+ throw new NullReferenceException($"{nameof(template.Id)} should have a value.");
+ }
+
+ var uri = new Uri(_baseAddress, $"v2/projects/{_projectId}/templates/{template.Id}");
+
+ _logger?.LogDebug("Updating a template with {templateId} in {projectId}", template.Id, _projectId);
+ return _http.Send(uri, HttpMethod.Put, template, cancellationToken: cancellationToken);
+ }
+
+ ///
+ public Task Delete(string templateId, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(templateId))
+ {
+ throw new ArgumentNullException(nameof(templateId));
+ }
+
+ var uri = new Uri(_baseAddress, $"v2/projects/{_projectId}/templates/{templateId}");
+
+ _logger?.LogDebug("Deleting a template with {templateId} in {projectId}", templateId, _projectId);
+ return _http.Send