Skip to content

Commit

Permalink
Feat/conversation event transcoding capability endpoints (#43)
Browse files Browse the repository at this point in the history
* refactor: make identities common

* feat: implement Events, transcoding add capabilities 

* chore: add APPLEBC to ConversationChannel.cs

* chore: move out capabilities

* chore: clean Var{property} from toStrings

* refactor: harmonize exception message between injectEvent and injectMessage
  • Loading branch information
Dovchik authored Mar 20, 2024
1 parent d351ff0 commit 58b2ece
Show file tree
Hide file tree
Showing 61 changed files with 1,641 additions and 47 deletions.
1 change: 1 addition & 0 deletions src/Sinch/Conversation/Apps/App.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Text;
using Sinch.Conversation.Common;

namespace Sinch.Conversation.Apps
{
Expand Down
1 change: 1 addition & 0 deletions src/Sinch/Conversation/Apps/Create/CreateAppRequest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Text;
using Sinch.Conversation.Common;

namespace Sinch.Conversation.Apps.Create
{
Expand Down
1 change: 1 addition & 0 deletions src/Sinch/Conversation/Apps/Update/UpdateAppRequest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization;
using Sinch.Conversation.Common;

namespace Sinch.Conversation.Apps.Update
{
Expand Down
58 changes: 58 additions & 0 deletions src/Sinch/Conversation/Capability/Capabilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Sinch.Core;
using Sinch.Logger;

namespace Sinch.Conversation.Capability
{
/// <summary>
/// A capability query checks the options available for reaching the
/// [contact](https://developers.sinch.com/docs/conversation/keyconcepts/#contact)
/// on the channels on which it has a channel identity.
/// Capability queries can only be executed for contacts that already exist in a project and app.
/// For executing the request, either the contact ID or the channel recipient identities of the contact are required.
/// The request is executed asynchronously, therefore the service responds immediately.
/// The result of the capability query is sent to the registered webhook for the CAPABILITY trigger.
/// </summary>
public interface ISinchConversationCapabilities
{
/// <summary>
/// This method is asynchronous - it immediately returns the requested Capability registration.
/// Capability check is then delivered as a callback to registered webhooks with trigger
/// CAPABILITY for every reachable channel.
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<LookupCapabilityResponse> Lookup(LookupCapabilityRequest request,
CancellationToken cancellationToken = default);
}

internal class Capabilities : ISinchConversationCapabilities
{
private readonly Uri _baseAddress;
private readonly IHttp _http;
private readonly ILoggerAdapter<ISinchConversationCapabilities> _logger;
private readonly string _projectId;

public Capabilities(string projectId, Uri baseAddress, ILoggerAdapter<ISinchConversationCapabilities> logger,
IHttp http)
{
_projectId = projectId;
_baseAddress = baseAddress;
_http = http;
_logger = logger;
}

public Task<LookupCapabilityResponse> Lookup(LookupCapabilityRequest request,
CancellationToken cancellationToken = default)
{
var uri = new Uri(_baseAddress, $"v1/projects/{_projectId}/capability:query");
_logger?.LogDebug("Looking up for a capability...");
return _http.Send<LookupCapabilityRequest, LookupCapabilityResponse>(uri, HttpMethod.Post, request,
cancellationToken);
}
}
}
30 changes: 30 additions & 0 deletions src/Sinch/Conversation/Capability/LookupCapabilityRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Sinch.Conversation.Common;

namespace Sinch.Conversation.Capability
{
public class LookupCapabilityRequest
{
/// <summary>
/// The ID of the app to use for capability lookup.
/// </summary>
#if NET7_0_OR_GREATER
public required string AppId { get; set; }
#else
public string AppId { get; set; }
#endif

/// <summary>
/// The recipient to lookup capabilities for. Requires either contact_id or identified_by.
/// </summary>
#if NET7_0_OR_GREATER
public required IRecipient Recipient { get; set; }
#else
public IRecipient Recipient { get; set; }
#endif

/// <summary>
/// ID for the asynchronous response, will be generated if not set. Currently this field is not used for idempotency.
/// </summary>
public string RequestId { get; set; }
}
}
30 changes: 30 additions & 0 deletions src/Sinch/Conversation/Capability/LookupCapabilityResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Sinch.Conversation.Common;

namespace Sinch.Conversation.Capability
{
public class LookupCapabilityResponse
{
/// <summary>
/// The ID of the app to use for capability lookup.
/// </summary>
#if NET7_0_OR_GREATER
public required string AppId { get; set; }
#else
public string AppId { get; set; }
#endif

/// <summary>
/// The recipient to lookup capabilities for. Requires either contact_id or identified_by.
/// </summary>
#if NET7_0_OR_GREATER
public required IRecipient Recipient { get; set; }
#else
public IRecipient Recipient { get; set; }
#endif

/// <summary>
/// ID for the asynchronous response, will be generated if not set. Currently this field is not used for idempotency.
/// </summary>
public string RequestId { get; set; }
}
}
22 changes: 22 additions & 0 deletions src/Sinch/Conversation/Common/Agent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Sinch.Conversation.Events.AppEvents;

namespace Sinch.Conversation.Common
{
/// <summary>
/// Represents an agent that is involved in a conversation.
/// </summary>
public class Agent
{
/// <summary>
/// Agent's display name
/// </summary>
public string DisplayName { get; set; }

public AgentType Type { get; set; }

/// <summary>
/// The Agent's picture url.
/// </summary>
public string PictureUrl { get; set; }
}
}
24 changes: 24 additions & 0 deletions src/Sinch/Conversation/Common/AgentType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
using Sinch.Core;

namespace Sinch.Conversation.Common
{
[JsonConverter(typeof(EnumRecordJsonConverter<AgentType>))]
public record AgentType(string Value) : EnumRecord(Value)
{
/// <summary>
/// The default AgentType. When the agent type is unknown.
/// </summary>
public static readonly AgentType UnknownAgentType = new("UNKNOWN_AGENT_TYPE");

/// <summary>
/// Human agent.
/// </summary>
public static readonly AgentType Human = new("HUMAN");

/// <summary>
/// Bot agent.
/// </summary>
public static readonly AgentType Bot = new("BOT");
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
using System.Collections.Generic;

namespace Sinch.Conversation.Messages
namespace Sinch.Conversation.Common
{
public class Identified : IRecipient
{
public IdentifiedBy IdentifiedBy { get; set; }
}

public class IdentifiedBy
{
public List<ChannelIdentity> ChannelIdentities { get; set; }
}

public class ChannelIdentity
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Sinch.Conversation.Messages
namespace Sinch.Conversation.Common
{
public sealed class ContactRecipient : IRecipient
{
Expand Down
9 changes: 9 additions & 0 deletions src/Sinch/Conversation/Common/IRecipient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Sinch.Core;

namespace Sinch.Conversation.Common
{
[JsonInterfaceConverter(typeof(InterfaceConverter<IRecipient>))]
public interface IRecipient
{
}
}
15 changes: 15 additions & 0 deletions src/Sinch/Conversation/Common/Identified.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using Sinch.Conversation.Messages;

namespace Sinch.Conversation.Common
{
public class Identified : IRecipient
{
public IdentifiedBy IdentifiedBy { get; set; }
}

public class IdentifiedBy
{
public List<ChannelIdentity> ChannelIdentities { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Text.Json.Serialization;
using Sinch.Core;

namespace Sinch.Conversation.Apps
namespace Sinch.Conversation.Common
{
/// <summary>
/// Represents the processing mode options for Conversation API.
Expand Down
1 change: 1 addition & 0 deletions src/Sinch/Conversation/Contacts/Contact.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinch.Conversation.Common;
using Sinch.Conversation.Messages;
using Sinch.Core;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Text;
using Sinch.Conversation.Common;
using Sinch.Conversation.Messages;

namespace Sinch.Conversation.Contacts.Create
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Sinch.Conversation.Messages;
using Sinch.Conversation.Common;
using Sinch.Conversation.Messages;

namespace Sinch.Conversation.Contacts.GetChannelProfile
{
Expand Down
7 changes: 6 additions & 1 deletion src/Sinch/Conversation/ConversationChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ public record ConversationChannel(string Value) : EnumRecord(Value)
/// WeChat channel.
/// </summary>
public static readonly ConversationChannel WeChat = new("WECHAT");


/// <summary>
/// AppleBC channel.
/// </summary>
public static readonly ConversationChannel AppleBC = new("APPLEBC");

/// <summary>
/// Channel has not been specified
/// </summary>
Expand Down
35 changes: 32 additions & 3 deletions src/Sinch/Conversation/Conversations/Conversations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using System.Web;
using Sinch.Conversation.Conversations.Create;
using Sinch.Conversation.Conversations.InjectEvent;
using Sinch.Conversation.Conversations.InjectMessage;
using Sinch.Conversation.Conversations.List;
using Sinch.Core;
Expand Down Expand Up @@ -92,6 +93,15 @@ Task<Conversation> Update(Conversation conversation,
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task InjectMessage(InjectMessageRequest injectMessageRequest, CancellationToken cancellationToken = default);

/// <summary>
/// This operation injects a conversation event into a specific conversation.
/// </summary>
/// <param name="injectEventRequest"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<InjectEventResponse> InjectEvent(InjectEventRequest injectEventRequest,
CancellationToken cancellationToken = default);
}

internal class ConversationsClient : ISinchConversationConversations
Expand Down Expand Up @@ -219,11 +229,11 @@ public Task InjectMessage(InjectMessageRequest injectMessageRequest,
CancellationToken cancellationToken = default)
{
if (injectMessageRequest == null)
throw new ArgumentNullException(nameof(injectMessageRequest), "Shouldn't be null");
throw new ArgumentNullException(nameof(injectMessageRequest), "Shouldn't be null.");

if (string.IsNullOrEmpty(injectMessageRequest.ConversationId))
throw new NullReferenceException(
$"{nameof(injectMessageRequest)}.{nameof(injectMessageRequest.ConversationId)} should have a value");
$"{nameof(injectMessageRequest)}.{nameof(injectMessageRequest.ConversationId)} is required.");

var uri = new Uri(_baseAddress,
$"v1/projects/{_projectId}/conversations/{injectMessageRequest.ConversationId}:inject-message");
Expand All @@ -232,5 +242,24 @@ public Task InjectMessage(InjectMessageRequest injectMessageRequest,
return _http.Send<InjectMessageRequest, object>(uri, HttpMethod.Post, injectMessageRequest,
cancellationToken);
}

/// <inheritdoc />
public Task<InjectEventResponse> InjectEvent(InjectEventRequest injectEventRequest,
CancellationToken cancellationToken = default)
{
if (injectEventRequest == null)
throw new ArgumentNullException(nameof(injectEventRequest), "Shouldn't be null");

if (string.IsNullOrEmpty(injectEventRequest.ConversationId))
throw new NullReferenceException(
$"{nameof(injectEventRequest)}.{nameof(injectEventRequest.ConversationId)} is required.");

var uri = new Uri(_baseAddress,
$"v1/projects/{_projectId}/conversations/{injectEventRequest.ConversationId}:inject-event");
_logger?.LogDebug("Injecting a message into {conversationId} of {project}",
injectEventRequest.ConversationId, _projectId);
return _http.Send<InjectEventRequest, InjectEventResponse>(uri, HttpMethod.Post, injectEventRequest,
cancellationToken);
}
}
}
Loading

0 comments on commit 58b2ece

Please sign in to comment.