Using ReactiveX and SignalR in WebApi - c#

I am trying to integrate ReactiveX and SingalR with WebApi following: http://www.thinqlinq.com/Post.aspx/Title/SignalR-and-Reactive-Extensions-are-an-Rx-for-server-push-notifications. I have got the service running using WebApi template, but I am not able to capture any data on the client side
Here is my code:
public class ObservableFaceID
{
public ObservableFaceID()
{
var rand = new Random(DateTime.Now.Millisecond);
var Generator = Observable.Generate<double, UserModel>(
initialState: 0,
condition: (val) => true,
iterate: (val) => rand.NextDouble(),
resultSelector: (val) => new UserModel
{
DateTime = DateTime.Now,
ID = Guid.NewGuid().ToString(),
Image = Convert.ToBase64String(Encoding.Default.GetBytes("Hello world"))
},
timeSelector: (val) => TimeSpan.FromSeconds(val));
Generator.Subscribe((value) =>
{
var context = GlobalHost.ConnectionManager.GetHubContext<ObservableFaceIdHub>();
context.Clients.All.Broadcast(value);
});
}
}
ObservableFaceIdHub
public class ObservableFaceIdHub : Hub
{
}
Global.asax:
protected void Application_Start()
{
faceID = new ObservableFaceID();
}
by depbugging the service, I can see that the service is working fine and I can see that it keeps generating a new data
but the Client (console application) is not capturing any data.:
static void Main(string[] args)
{
var cn = new HubConnection("http://localhost:44302/");
var sensor = cn.CreateHubProxy("ObservableFaceIdHub");
sensor.On<UserModel>("Broadcast", (item) => Console.WriteLine(item.ID));
sub = sensor.Subscribe("Broadcast");
sub.Received += Sub_Received;
cn.Start().Wait();
Console.ReadLine();
}
private static void Sub_Received(IList<Newtonsoft.Json.Linq.JToken> obj)
{
}
Did I miss something?
I can point out that the only changes between my code and the referred code is in this line:
this is my line:
context.Clients.All.Broadcast(value)
the reference line:
context.Clients.Broadcast(value)

Related

Authenticating a Callback URL in a REST API

We are testing Azure Communication Services in a new project. Specifically, we are looking at the Azure Communication Services for Calling documented here and the quick start project found here.
The general pattern to utilize the service is shown in the following code.
public string AppCallbackUrl => $"{AppBaseUrl}/api/outboundcall/callback?{EventAuthHandler.GetSecretQuerystring}"
// Defined the call with a Callback URL
var source = new CommunicationUserIdentifier(callConfiguration.SourceIdentity);
var target = new PhoneNumberIdentifier(targetPhoneNumber);
var createCallOption = new CreateCallOptions(
new Uri(AppCallbackUrl),
new List<MediaType> { MediaType.Audio },
new List<EventSubscriptionType> { EventSubscriptionType.DtmfReceived });
// Initiate the call
var call = await callClient.CreateCallConnectionAsync(
source, new List<CommunicationIdentifier>() { target }, createCallOption, reportCancellationToken)
.ConfigureAwait(false);
// Register for call back events
RegisterToCallStateChangeEvent(call.Value.CallConnectionId);
The example uses a configuration value or hardcoded secret key to authenticate the Callback Url, as shown below.
[Route("api/[controller]")]
[ApiController]
public class OutboundCallController : ControllerBase
{
[AllowAnonymous]
[HttpPost("callback")]
public async Task<IActionResult> OnIncomingRequestAsync()
{
// Validating the incoming request by using secret set in app.settings
if (EventAuthHandler.Authorize(Request))
{
...
}
else
{
return StatusCode(StatusCodes.Status401Unauthorized);
}
}
}
public class EventAuthHandler
{
private static readonly string SecretKey = "secret";
private static readonly string SecretValue;
static EventAuthHandler()
{
SecretValue = ConfigurationManager.AppSettings["SecretPlaceholder"] ?? "h3llowW0rld";
}
public static bool Authorize(HttpRequest request)
{
if (request.QueryString.Value != null)
{
var keyValuePair = HttpUtility.ParseQueryString(request.QueryString.Value);
return !string.IsNullOrEmpty(keyValuePair[SecretKey]) && keyValuePair[SecretKey].Equals(SecretValue);
}
return false;
}
public static string GetSecretQuerystring => $"{SecretKey}={HttpUtility.UrlEncode(SecretValue)}";
}
Is there a better way to do this in a production environment? How can I incorporate ASP.NET Core authentication with a Callback?

Mocking HttpContext

I'm trying to write an Unit Test for my ASP.Net Core application with XUnit framework and MOQ and am trying to test the below method(snippet given below):
public async Task<IActionResult> Save([FromBody] DTO.ContactUs contactUs)
{
contactUs.FirstName = _htmlEncoder.Encode(contactUs.FirstName);
contactUs.LastName = _htmlEncoder.Encode(contactUs.LastName);
contactUs.EmailAddress = _htmlEncoder.Encode(contactUs.EmailAddress);
contactUs.Phone = _htmlEncoder.Encode(contactUs.Phone);
if (HttpContext.User.CurrentClient() != null)
contactUs.ClientId = HttpContext.User.CurrentClient().ClientId;
contactUs.UserId = User.GetUserId();
string dbName = HttpContext.User.CurrentClient().ConnectionString;
var result = _clientService.AddNewContactUs(contactUs, dbName);
if (result)
{
try
{
int clientId = HttpContext.User.CurrentClient().ClientId;
var clientDetails = _clientService.GetClientDetailsByClientID(clientId);
// Lines of code...
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
return Json(result);
}
While I can mock all the other dependent services, I'm kind of stuck with the HttpContext part. I am not able to mock the HttpContext.User.CurrentClient() part where HttpContext.User is of type ClaimsPrincipal and CurrentClient is an user-defined function, defined as:
public static Client CurrentClient(this ClaimsPrincipal principal)
{
if (!string.IsNullOrEmpty(principal.Claims.Single(p => p.Type.Equals(AppClaimTypes.CurrentClient)).Value))
{
int clientId = Convert.ToInt32(principal.Claims.Single(p => p.Type.Equals(AppClaimTypes.CurrentClient)).Value);
return principal.GetClients().Where(c => c.ClientId == clientId).FirstOrDefault();
}
else
{
return null;
}
}
This is my UnitTest class that I have managed to write till now:
public class ContactUsControllerTests
{
private Mock<IClientService> clientServiceMock;
private Mock<IWebHostEnvironment> webHostEnvironmentMock;
private Mock<HtmlEncoder> htmlEncoderObjMock;
private Mock<IEmailNotification> emailNotificationMock;
private Mock<HttpContext> mockContext;
private Mock<HttpRequest> mockRequest;
private Mock<ClaimsPrincipal> mockClaimsPrincipal;
private ContactUs contactUsObj = new ContactUs()
{
FirstName = "TestFN",
LastName = "TestLN",
EmailAddress = "testemail#gmail.com",
Phone = "4564560000",
Comments = "This is just a test"
};
private ClaimsPrincipal principal = new ClaimsPrincipal();
public ContactUsControllerTests()
{
clientServiceMock = new Mock<IClientService>();
webHostEnvironmentMock = new Mock<IWebHostEnvironment>();
htmlEncoderObjMock = new Mock<HtmlEncoder>();
emailNotificationMock = new Mock<IEmailNotification>();
mockRequest = new Mock<HttpRequest>();
mockContext = new Mock<HttpContext>();
// set-up htmlEncoderMock
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.FirstName)).Returns(contactUsObj.FirstName);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.LastName)).Returns(contactUsObj.LastName);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.EmailAddress)).Returns(contactUsObj.EmailAddress);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.Phone)).Returns(contactUsObj.Phone);
htmlEncoderObjMock.Setup(h => h.Encode(contactUsObj.Comments)).Returns(contactUsObj.Comments);
// set-up mockContext
mockContext.Setup(m => m.Request).Returns(mockRequest.Object);
mockContext.Object.User.CurrentClient().ClientId = 30; // this throws error
//other initialisations
}
[Fact]
public async void SaveMethodTest()
{
ContactUsController contactUsControllerObj = new ContactUsController(clientServiceMock.Object, webHostEnvironmentMock.Object, htmlEncoderObjMock.Object, emailNotificationMock.Object);
// Act
await contactUsControllerObj.Save(contactUsObj);
// Arrange
// Lines of code
}
}
Any help whatsoever on this would very helpful.

Signalr hub with Unit Testing

I am using nUnit Test and Signalr with .net framework 4.5 and I get error like :
System.Net.Http.HttpRequestException : An error occurred while sending the request.
----> System.Net.WebException : Unable to connect to the remote server
----> System.Net.Sockets.SocketException : No connection could be made because the target machine actively refused it 127.0.0.1:6790
Actually I need to test when hub.GetCallControlData(); being called then it should be invoked SetServer method
code :
[HubName("SignalRHub")]
public class SignalRHub1 : Hub
{
public override Task OnConnected()
{
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
return base.OnDisconnected(stopCalled);
}
public void GetCallControlData()
{
Clients.Caller.SetServer("Server");
}
}
Test
[TestFixture]
public class SignalrHubTest1
{
public interface IClientContract
{
void SetServer(string s);
}
[Test]
public async Task MockSingalrHub()
{
var url = "http://localhost:6790";
var connectionId = Guid.NewGuid().ToString();
var mockRequest = new Mock<IRequest>();
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
var mockProxy = new Mock<IHubProxy>();
var _connection = new HubConnection(url);
var clientContract = new Mock<IClientContract>();
var mockHeaders = new Mock<INameValueCollection>();
mockHeaders.Setup(h => h["host"]).Returns(url);
mockRequest.Setup(r => r.Headers).Returns(mockHeaders.Object);
clientContract.Setup(_ => _.SetServer(It.IsAny<string>()));
mockClients.Setup(m => m.Caller).Returns(clientContract.Object);
var hub = new SignalRHub1()
{
Clients = mockClients.Object,
Context = new HubCallerContext(mockRequest.Object, connectionId)
};
var _hub = _connection.CreateHubProxy("SignalRHub");
mockProxy.Setup(x => x.Subscribe(It.IsAny<string>())).Returns(new Subscription());
_hub.On<string>("SetServer", x => SetServer(x));
await hub.OnConnected();
hub.GetCallControlData();
clientContract.VerifyAll();
await _connection.Start();
}
internal void SetServer(string s)
{
// throw new NotImplementedException();
}
}
Unit tests are meant to be isolated. There is no need to connect to an actual server in order to verify expected behavior.
Given the shown Hub,
[HubName("SignalRHub")]
public class SignalRHub1 : Hub {
public void GetCallControlData() {
Clients.Caller.SetServer("Server");
}
}
the following isolated unit test behaves as expected and verifies that the SetServer("Server") is invoked.
[TestClass]
public class SignalrHub1Tests {
public interface IClientContract {
void SetServer(string s);
}
[TestMethod]
public void GetCallControlData_Should_SetServer() {
//Arrange
var contract = new Mock<IClientContract>();
contract.Setup(_ => _.SetServer(It.IsAny<string>()));
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
mockClients.Setup(_ => _.Caller).Returns(contract.Object);
var hub = new SignalRHub1() {
Clients = mockClients.Object
};
//Act
hub.GetCallControlData();
//Assert
contract.Verify(_ => _.SetServer("Server"));
}
}

Couchbase Lite 2.1 Replicator Issue .net

We have just upgraded our SyncGatewaty to 2.1. So now I’m refactoring our client code to use CouchbaseLite 2.1. When I try to replicate I get the error:
Got LiteCore error: Not Found (6/404)
I originally got the error when connecting to our Dev Server, and then installed a local clean copy on my laptop and I get the same error when trying to connect to it too.
Log:
INFO) Couchbase 2019-01-10T10:56:47.8503147-07:00 (Startup) [1] CouchbaseLite/2.1.2 (.NET; Microsoft Windows 10.0.17763 ) Build/13 LiteCore/ (15) Commit/9aebf28
WARNING) LiteCore 2019-01-10T10:56:48.1943139-07:00 {C4SocketImpl#1}==> class litecore::repl::C4SocketImpl ws://localhost.com:443//_blipsync
WARNING) LiteCore 2019-01-10T10:56:48.1943139-07:00 {C4SocketImpl#1} Unexpected or unclean socket disconnect! (reason=WebSocket status, code=404)
ERROR) Sync 2019-01-10T10:56:48.1993137-07:00 {Repl#2}==> class litecore::repl::Replicator c:\temp\content_meta_data.cblite2\ ->ws://localhost:443//_blipsync
ERROR) Sync 2019-01-10T10:56:48.1993137-0
7:00 {Repl#2} Got LiteCore error: Not Found (6/404)
My code:
using System;
using System.IO;
using Couchbase.Lite;
using Couchbase.Lite.Support;
using Couchbase.Lite.Sync;
using NLog;
namespace ReplicatorExample
{
public class DatabaseManager
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
public const string BUCKET_CONTENT_META_DATA = "content_meta_data";
private static DatabaseManager _instance;
public static DatabaseManager GetInstance()
{
NetDesktop.Activate();
NetDesktop.EnableTextLogging("logs");
return _instance ?? (_instance = new DatabaseManager());
}
public void InitializeBuckets()
{
try
{
var defaultAuthenticator = GetDefaultAuthenticator();
var dirInfo = new DirectoryInfo($"c:\\temp\\{BUCKET_CONTENT_META_DATA}");
if (!dirInfo.Parent.Exists)
{
dirInfo.Parent.Create();
}
var database = new Database(dirInfo.FullName);
// Create replicator to push and pull changes to and from the cloud
var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4985"));
var replConfig = new ReplicatorConfiguration(database, targetEndpoint)
{
Authenticator = defaultAuthenticator,
Continuous = true,
//Channels = new List<string>
//{
// "approved",
//
//}
};
var replicator = new Replicator(replConfig);
replicator.AddChangeListener((sender, args) =>
{
if (args.Status.Error != null)
{
_log.Error($"{args.Status.Error}");
}
else
{
_log.Debug(args.Status);
}
});
replicator.Start();
}
catch (Exception e)
{
_log.Error(e);
}
}
private Authenticator GetDefaultAuthenticator()
{
return new BasicAuthenticator("BigD","123456");
}
}
}
I believe you need to specify the database name in the URL for targetEndpoint.
E.g: var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4984/mydatabase"));

Unable to receive events from server in ServiceStack

i'm having problem using events in my servicestack application.
I'm creating an SOA applicatin based on ServiceStack. I've had no problem creating a simple GET/POST manager within the host.
Now i would like to add events
I'm trying using an example, but the event is not received by the client
Does someone have an idea about that?
This is my server:
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
ServerEventsFeature serverEventsFeature = new ServerEventsFeature()
{
LimitToAuthenticatedUsers = false,
NotifyChannelOfSubscriptions = true,
OnPublish = (res, msg) =>
{
//fired after ever message is published
res.Write("\n\n\n\n\n\n\n\n\n\n");
res.Flush();
},
OnConnect = (eventSubscription, dictionary) =>
{
},
OnSubscribe = (eventSubscription) =>
{
}
};
Plugins.Add(serverEventsFeature);
container.Register<IServerEvents>(c => new MemoryServerEvents());
container.Register(c => new FrontendMessages(c.Resolve<IServerEvents>()));
container.Register<IWebServiceEventManager>(c => new WebServiceEventManager(DeviceManager, macroManager));
SetConfig(new HostConfig
{
DefaultContentType = MimeTypes.Json,
EnableFeatures = Feature.All.Remove(Feature.Html),
});
public class FrontendMessage
{
public string Level { get; set; }
public string Message { get; set; }
}
public class FrontendMessages
{
private readonly IServerEvents _serverEvents;
private Timer _timer;
public FrontendMessages(IServerEvents serverEvents)
{
if (serverEvents == null) throw new ArgumentNullException(nameof(serverEvents));
_serverEvents = serverEvents;
}
public void Start()
{
var ticks = 0;
_timer = new Timer(_ => {
Info($"Tick {ticks++}");
_timer.Change(500, Timeout.Infinite);
}, null, 500, Timeout.Infinite);
}
public void Info(string message, params object[] parameters)
{
var frontendMessage = new FrontendMessage
{
Level = "success",
Message = message
};
Console.WriteLine("Sending message: " + frontendMessage.Message);
_serverEvents.NotifyChannel("messages", frontendMessage);
}
This is my client:
public async void Connect()
{
try
{
Task.Delay(2000).Wait();
clientEvents = new ServerEventsClient("http://127.0.0.1:20001/", "messages");
clientEvents.OnConnect = (msg) =>
{
};
clientEvents.OnHeartbeat = () =>
{
};
clientEvents.OnCommand = (msg) =>
{
};
clientEvents.OnException = (msg) =>
{
};
clientEvents.OnMessage = (msg) =>
{
};
Dictionary<string, ServerEventCallback> handlers = new Dictionary<string, ServerEventCallback>();
handlers.Add("messages", (client, msg) =>
{
});
clientEvents.RegisterHandlers(handlers);
await clientEvents.Connect();
client = (IServiceClient)(clientEvents.ServiceClient);
}
catch (Exception e)
{
}
}
I'd first recommend looking at ServerEvents Examples and the docs for the C# ServerEventsClient for examples of working configurations.
Your extra ServerEventsFeature configuration isn't useful as you're just specifying the defaults and the Publish() new-line hack is not needed when you disable buffering in ASP.NET. So I would change it to:
Plugins.Add(new ServerEventsFeature());
Second issue is that you're use of Message Event handlers is incorrect, your C# ServerEventsClient is already connected to the messages channel. Your handlers is used to listen for messages sent to the cmd.* selector (e.g. cmd.FrontendMessage).
Since you're publishing a DTO to a channel, i.e:
_serverEvents.NotifyChannel("messages", frontendMessage);
You can use a Global Receiver to handle it, e.g:
public class GlobalReceiver : ServerEventReceiver
{
public void Any(FrontendMessage request)
{
...
}
}
client.RegisterReceiver<GlobalReceiver>();
Thanks mythz!
It works correectly.
Next step is to replicate the same behaviour on javascript client (events and get/post request). Do you have something to suggest me?
Thanks a lot!
Leo

Categories

Resources