Skip to content

Commit

Permalink
feat: add OmniMessageOverride (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dovchik authored May 6, 2024
1 parent 7d11e70 commit 8bd781b
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 13 deletions.
6 changes: 2 additions & 4 deletions Sinch.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml"
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=connectconf/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Omni/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=onhold/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=svamlet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kakao/@EntryIndexedValue">True</s:Boolean>
Expand Down
13 changes: 13 additions & 0 deletions src/Sinch/Conversation/Messages/Message/AppMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ public AppMessage(ContactInfoMessage contactInfoMessage)
/// </summary>
public Dictionary<ConversationChannel, IChannelSpecificMessage>? ChannelSpecificMessage { get; set; }


public Dictionary<ChannelSpecificTemplate, IOmniMessageOverride>? ExplicitChannelOmniMessage { get; set; }

/// <inheritdoc cref="Agent" />
public Agent? Agent { get; set; }
}
Expand All @@ -135,6 +138,16 @@ public interface IChannelSpecificMessage
public MessageType MessageType { get; }
}



[JsonConverter(typeof(EnumRecordJsonConverter<ChannelSpecificTemplate>))]
public record ChannelSpecificTemplate(string Value) : EnumRecord(Value)
{
public static readonly ChannelSpecificTemplate WhatsApp = new ChannelSpecificTemplate("WHATSAPP");
public static readonly ChannelSpecificTemplate KakaoTalk = new ChannelSpecificTemplate("KAKAOTALK");
public static readonly ChannelSpecificTemplate WeChat = new ChannelSpecificTemplate("WECHAT");
}

public class ChannelSpecificMessageJsonInterfaceConverter : JsonConverter<IChannelSpecificMessage>
{
public override IChannelSpecificMessage Read(ref Utf8JsonReader reader, Type typeToConvert,
Expand Down
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/CardMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Sinch.Conversation.Messages.Message
/// <summary>
/// Message containing text, media and choices.
/// </summary>
public sealed class CardMessage
public sealed class CardMessage : IOmniMessageOverride
{
/// <summary>
/// Gets or Sets Height
Expand Down
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/CarouselMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Sinch.Conversation.Messages.Message
{
public class CarouselMessage
public sealed class CarouselMessage : IOmniMessageOverride
{
/// <summary>
/// A list of up to 10 cards.
Expand Down
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/ChoiceMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Sinch.Conversation.Messages.Message
{
public class ChoiceMessage
public sealed class ChoiceMessage : IOmniMessageOverride
{
/// <summary>
/// The number of choices is limited to 10.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Sinch.Conversation.Messages.Message
/// <summary>
/// Message containing contact information.
/// </summary>
public sealed class ContactInfoMessage
public sealed class ContactInfoMessage : IOmniMessageOverride
{
/// <summary>
/// Gets or Sets Name
Expand Down
65 changes: 65 additions & 0 deletions src/Sinch/Conversation/Messages/Message/IOmniMessageOverride.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Sinch.Core;

namespace Sinch.Conversation.Messages.Message
{
[JsonInterfaceConverter(typeof(OmniMessageOverrideJsonConverter))]
public interface IOmniMessageOverride
{
}

public class OmniMessageOverrideJsonConverter : JsonConverter<IOmniMessageOverride>
{
private readonly Dictionary<string, Type> _propNameToTypeMap = new()
{
{ "text_message", typeof(TextMessage) },
{ "media_message", typeof(MediaMessage) },
{ "choice_message", typeof(ChoiceMessage) },
{ "card_message", typeof(CardMessage) },
{ "carousel_message", typeof(CarouselMessage) },
{ "location_message", typeof(LocationMessage) },
{ "contact_info_message", typeof(ContactInfoMessage) },
{ "list_message", typeof(ListMessage) },
{ "template_reference", typeof(TemplateReference) },
};

public override IOmniMessageOverride? Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
var elem = JsonElement.ParseValue(ref reader);

foreach (var entry in _propNameToTypeMap)
{
if (elem.TryGetProperty(entry.Key, out var value))
{
return value.Deserialize(entry.Value, options) as IOmniMessageOverride;
}
}

throw new JsonException(
$"Failed to match {nameof(IOmniMessageOverride)}, got json element: {elem.ToString()}");
}

public override void Write(Utf8JsonWriter writer, IOmniMessageOverride value, JsonSerializerOptions options)
{
var type = value.GetType();
var matchingType = _propNameToTypeMap.FirstOrDefault(x => x.Value == type);
if (matchingType.Key is null)
{
throw new InvalidOperationException(
$"Value is not in range of expected types - actual type is {type.FullName}");
}


JsonSerializer.Serialize(writer, new Dictionary<string, object>
{
// dynamically cast IOmniMessageTemplate to specific type, e.g. TextMessage so avoid recursive infinite write
{ matchingType.Key, Convert.ChangeType(value, type) }
}, options);
}
}
}
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/ListMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Sinch.Conversation.Messages.Message
/// <summary>
/// A message containing a list of options to choose from
/// </summary>
public sealed class ListMessage
public sealed class ListMessage : IOmniMessageOverride
{
/// <summary>
/// A title for the message that is displayed near the products or choices.
Expand Down
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/LocationMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Sinch.Conversation.Messages.Message
/// <summary>
/// Message containing geographic location.
/// </summary>
public sealed class LocationMessage
public sealed class LocationMessage : IOmniMessageOverride
{
/// <summary>
/// Gets or Sets Coordinates
Expand Down
6 changes: 5 additions & 1 deletion src/Sinch/Conversation/Messages/Message/MediaMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Sinch.Conversation.Messages.Message
/// <summary>
/// A message containing a media component, such as an image, document, or video.
/// </summary>
public sealed class MediaMessage
public sealed class MediaMessage : IOmniMessageOverride
{
/// <summary>
/// An optional parameter. Will be used where it is natively supported.
Expand All @@ -23,6 +23,10 @@ public sealed class MediaMessage
public Uri Url { get; set; } = null!;
#endif

/// <summary>
/// Overrides the media file name.
/// </summary>
public string? FilenameOverride { get; set; }

/// <summary>
/// Returns the string presentation of the object
Expand Down
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/TemplateMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public override string ToString()
/// 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.
/// </summary>
public sealed class TemplateReference
public sealed class TemplateReference : IOmniMessageOverride
{
/// <summary>
/// The ID of the template.
Expand Down
2 changes: 1 addition & 1 deletion src/Sinch/Conversation/Messages/Message/TextMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Sinch.Conversation.Messages.Message
/// <summary>
/// A message containing only text.
/// </summary>
public sealed class TextMessage
public sealed class TextMessage : IOmniMessageOverride
{
/// <summary>
/// A message containing only text.
Expand Down
64 changes: 64 additions & 0 deletions tests/Sinch.Tests/Conversation/MessagesTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
Expand All @@ -13,6 +14,7 @@
using Sinch.Conversation.Messages.List;
using Sinch.Conversation.Messages.Message;
using Sinch.Conversation.Messages.Message.ChannelSpecificMessages.WhatsApp;
using Sinch.Core;
using Xunit;

namespace Sinch.Tests.Conversation
Expand Down Expand Up @@ -356,5 +358,67 @@ public void DeserializeFlowMessage()
var dict = JsonSerializer.Deserialize<Dictionary<ConversationChannel, IChannelSpecificMessage>>(json);
dict[ConversationChannel.WhatsApp].Should().BeEquivalentTo(_flowMessage);
}


[Theory]
[ClassData(typeof(OmniMessageTestData))]
public void DeserializeOmniMessageOverride(string json, object dataToCheck)
{
var dict = JsonSerializer
.Deserialize<Dictionary<ChannelSpecificTemplate, IOmniMessageOverride>>(json,
options: new JsonSerializerOptions()
{
PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance
});
dict.Should().ContainKey(ChannelSpecificTemplate.WhatsApp).WhoseValue.Should()
.BeEquivalentTo(dataToCheck);
}
}

public class OmniMessageTestData : IEnumerable<object[]>
{
private static readonly string Text = @"
{
""WHATSAPP"": {
""text_message"": {
""text"": ""hello""
}
}
}";
private static readonly string Media = @"
{
""WHATSAPP"": {
""media_message"": {
""url"": ""https://hello.net""
}
}
}";
private static readonly string Template = @"
{
""WHATSAPP"": {
""template_reference"": {
""template_id"": ""id"",
""version"": ""3""
}
}
}";

private readonly List<object[]> _data = new()
{
new object[] { Text, new TextMessage("hello") },
new object[] { Media, new MediaMessage()
{
Url = new Uri("https://hello.net")
}},
new object[] { Template, new TemplateReference()
{
TemplateId = "id",
Version = "3"
}},
};

public IEnumerator<object[]> GetEnumerator() => _data.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
15 changes: 15 additions & 0 deletions tests/Sinch.Tests/Conversation/SendMessageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,16 @@ public async Task SendAllParams()
_baseMessageExpected.ttl = "1800s";
_baseMessageExpected.queue = "HIGH_PRIORITY";
_baseMessageExpected.message_metadata = "meta";
_baseMessageExpected.message.explicit_channel_omni_message = new
{
WECHAT = new
{
text_message = new
{
text = "hello"
}
}
};

_baseRequest.Recipient = new Identified
{
Expand All @@ -474,6 +484,11 @@ public async Task SendAllParams()
_baseRequest.Ttl = "1800s";
_baseRequest.Queue = MessageQueue.HighPriority;
_baseRequest.MessageMetadata = "meta";
_baseRequest.Message.ExplicitChannelOmniMessage =
new Dictionary<ChannelSpecificTemplate, IOmniMessageOverride>()
{
{ ChannelSpecificTemplate.WeChat, new TextMessage("hello") }
};

HttpMessageHandlerMock
.When(HttpMethod.Post,
Expand Down

0 comments on commit 8bd781b

Please sign in to comment.