For a few months now I have been using the ktor framework to create servers that expose rest calls and communication via webSockets. For now I have always used clients using kotlin as a programming language (or Android App, or App Desktop).
Specifically, I had a class that was injected with the HttpClient object (from the documentation = Asynchronous client to perform HTTP requests).
Within this class I have 4 methods:
start the session: instantiate the WebSocketSession object (Represents a web socket session between two peers)
send Frame
receives Frame
close the session
In Ktor my class is something that looks a lot like this:
class WebSocketServiceImpl(
private val client: HttpClient
){
private var socket: WebSocketSession? = null
//1)
suspend fun initSession(username: String): Resource<Unit>{
socket = client.webSocketSession {
url("ws://xxx.xxx.xxx.xxx:xxxx/myRoute?username=$username")
}
//2)
suspend fun send(myObj: MyObj) {
try {
val myObjSerialized = Json.encodeToString(myObj)
socket?.send(Frame.Text(myObjSerialized ))
} catch (e: Exception) {
e.printStackTrace()
}
}
//3)
fun observePrintableMessages(): Flow<MyObj> {
return try {
socket?.incoming
?.receiveAsFlow()
?.filter { it is Frame.Text }
?.map {
val myObjString = (it as? Frame.Text)?.readText() ?: ""
val printableMessageDto = Json.decodeFromString<MyObj>(myObjString)
} ?: flow { }
} catch (e: Exception) {
e.printStackTrace()
flow { }
}
}
//4)
suspend fun closeSession() {
socket?.close()
}
}
From the C # documentation instead, I found these examples on how to use Client-side WebSockets:
//1)
const exampleSocket = new WebSocket("wss://www.example.com/socketserver", "protocolOne");
//2)
exampleSocket.send("Here's some text that the server is urgently awaiting!");
//3)
exampleSocket.onmessage = (event) => {
console.log(event.data);
}
//4)
exampleSocket.close();
Admitted and not granted that the methods I found in C # really work, to make the WebSocket object used in C # be equivalent to the WebSocketSession object in Kotlin is enough for me to do so? :
public void initSession(string username)
{
exampleSocket = new WebSocket($"wss://www.example.com/socketserver?username={username}", "");
}
Or is it some other type of object to use?
If for any reason you don't know the answer, you don't need to vote negative, you can just move on.
I used the Websocket.Client library (by Mariusz Kotas) found on NuGet
public class WebSocketService : IWebSocketService
{
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
private void FireMessageReceivedEvent(Message message) => MessageReceived?.Invoke(this, new MessageReceivedEventArgs(message));
public string Url { get => "ws://192.168.1.202:8082/chat-socket"; }
private WebsocketClient webSocketClient;
public async Task<SessionResoult> InitSession(string username)
{
string usernameSession = $"?username={username}";
string urlWithUsername = $"{Url}{usernameSession}";
try
{
webSocketClient = new WebsocketClient(new Uri(urlWithUsername));
await webSocketClient.Start();
if (webSocketClient.IsRunning)
{
SubscribeNewMessages();
return new SessionResoult.Success();
}
else
{
return new SessionResoult.Error("webSocketClient is not running");
}
}
catch(Exception ex)
{
return new SessionResoult.Error(ex.Message);
}
}
private void SubscribeNewMessages()
{
webSocketClient.MessageReceived.Subscribe(m =>
{
MessageDto message = JsonConvert.DeserializeObject<MessageDto>(m.Text);
FireMessageReceivedEvent(message.ToMessage());
});
}
public async Task SendMessageAsync(string message)
{
await Task.Run(() => webSocketClient.Send(message));
}
public void CloseSession()
{
webSocketClient.Dispose();
}
}
In the code, the interesting parts are:
1) initialization of the WebsocketClient object
2) the subscription of receiving messages ( Start() method immediately after initialization)
3) observation of message subscription -> webSocketClient.MessageReceived.Subscribe
4) the 'Fire' of the event linked to the observation of messages -> FireMessageReceivedEvent
5) those who use the class must subscribe to the event of the latter ->
webSocketService.MessageReceived + = (sender, e) => {OnMessageReceived (e.MessageReceived); };
MessageReceivedEventArgs -> Class describing the Arguments of the event
SessionResoult -> Class similar to an Enum but with the possibility of passing a string or not based on which subclass it is
Related
I am trying to perform an action right after returning my response to a GraphQL API request.
Let me explain: I would like to develop a GraphQL server that receives a certain request from another server and sends a request to this other server just after answering the first request
In comparison, it looks like the OnActionExecuted function with ActionFilterAttribute but I can't implement it for GraphQl, maybe it works only for Rest APIs
I also found the function :
public async override Task AfterExecutionAsync(IExecutionContext context) from class GraphQL.Execution.DocumentExecutionListenerBase but I can't figure out how to implement it so that it triggers after returning my answer
this is the idea but of course it is not what i have tried haha
public async Task<sendLinkRequest> sendLinkStatusUpdate(newLinkStatusInput input)
{
return _sendLinkRequestService.sendLinkStatusUpdate(input);
//Doing this after my function return the result
var graphQLClient = new GraphQLHttpClient("https://exemple.com", new NewtonsoftJsonSerializer());
var sendLinkStatusUpdate = new GraphQLRequest
{
Query = "mutation{sendLinkStatusUpdate(input : {newLinkStatus: \"active\"}) {errors {scope message}}}",
Variables = new
{
newLinkStatus = "active"
}
};
var graphQLResponse = await graphQLClient.SendQueryAsync<sendLinkRequest>(sendLinkStatusUpdate);
}
Anyway, if someone has an idea or a lead I'm interested
Thanks for your comment ! I managed to do it like that :)
public sendLinkRequest sendLinkStatusUpdate(newLinkStatusInput input)
{
TrackingActionFilter ta = new TrackingActionFilter();
ta.ProcessCompleted += ta_nextAction; // register with an event
sendLinkRequest test = ta.StartProcess(input, _sendLinkRequestService);
return test;
}
public async static void ta_nextAction(object sender, EventArgs e)
{
var graphQLClient = new GraphQLHttpClient("https://talentusnewdemo.eu-west-1.beepleapp.eu/b/multi-tenant-api", new NewtonsoftJsonSerializer());
var sendLinkStatusUpdate = new GraphQLRequest
{
Query = "mutation{sendLinkStatusUpdate(input : {newLinkStatus: \"active\"}) {errors {scope message}}}",
Variables = new
{
newLinkStatus = "active"
}
};
var graphQLResponse = await graphQLClient.SendQueryAsync<sendLinkRequest>(sendLinkStatusUpdate);
Console.WriteLine("Process Completed!");
}
public class TrackingActionFilter
{
public event EventHandler ProcessCompleted;
public sendLinkRequest StartProcess(newLinkStatusInput input, IsendLinkRequestService _sendLinkRequestService)
{
Console.WriteLine("Process Started!");
OnProcessCompleted(EventArgs.Empty); //No event data
return _sendLinkRequestService.sendLinkStatusUpdate(input);
}
protected virtual void OnProcessCompleted(EventArgs e)
{
ProcessCompleted?.Invoke(this, e);
}
}
I want to be able to perform workloads at intervals.
I want to be able to make this class generic so I can pass it whatever "workload" I want and my timer function just does it. I also would like a means of "returning" the workload response back to the caller.
As an example. Let's say I have a series of classes I have built that download data from a JSON API, or scrape a web page. This web scraper/API downloader needs to download pages from a site at different intervals. Each page will take a different number of parameters. I have found something online that indicates setting the Elapsed event to a delegate. This "may"work but I need to have the passed in delegate "dynamic" itself. So the Start method below which accepts a Func won't be correct from a "generic" standpoint, which is what I am after.
The solution itself is just an example of a line of thinking. Am open to other generic alternatives that help me achieve this.
public abstract class TimerWorkerDelegate : IDisposable, ITimerWorker
{
protected System.Timers.Timer DataTimer;
public virtual void Start(Func<string> callback,double interval)
{
DataTimer = new System.Timers.Timer();
DataTimer.Interval = interval;
DataTimer.Elapsed += delegate {
callback();
};
if (!DataTimer.Enabled)
DataTimer.Enabled = true;
//IDisposable code
}
}
I might not understand 100% what you are REALLY trying to achieve, but... maybe something like.
public class Worker<T>
{
public event EventHandler<T> OnCompleted;
public Worker()
{}
public Worker(Func<T> fn, int interval)
{
Func = fn;
Interval = interval;
}
public async void Start()
{
if (Func == null)
throw new ArgumentNullException();
while (true)
{
await Task.Delay(Interval);
try
{
var result = Func();
OnCompleted(this, result);
}
catch
{
return; // handle
}
}
}
public Func<T> Func { get; set; }
public int Interval { get; set; }
}
And then usage in Console tester app as
public static void Main(string[] args)
{
var worker = new Worker<string>
{
Interval = 1000,
Func = () => { return string.Format("did some work at {0}", DateTime.Now); }
};
worker.OnCompleted += (sender, result) => { Console.WriteLine(result); };
worker.Start();
Console.ReadLine();
}
If you're open to using a library you could look at System.Reactive
With it you could setup something very easily to accomplish what you are looking to do.
Below is a very rudimentary implementation of something that could work for you:
void Main()
{
var scheduled = Schedule(
TimeSpan.FromSeconds(1),
() => Console.WriteLine($"The current time is: {DateTime.Now}"));
Console.ReadLine();
// Dispose will stop the scheduled action
scheduled.Dispose();
}
public IDisposable Schedule<T>(TimeSpan interval, Func<T> func)
=> Observable.Interval(interval).Subscribe(_ => func());
public IDisposable Schedule(TimeSpan interval, Action action)
=> Observable.Interval(interval).Subscribe(_ => action());
I have a requirement to connect to the server and collect data for processing. Below is my core class, which is responsible for looping through all the servers and try connecting them for processing.
public class CoreDataProcessingEngine : ICoreDataProcessingEngine
{
private readonly COMLib.ServerGateway _aServerGw;
private COMLib.ServerErrorInfo _aServerErrorInfo;
Public CoreDataProcessingEngine()
{
_aServerGw = new COMLib.ServerGateway();
_aServerErrorInfo = new COMLib.ServerErrorInfo();
}
//When service starts, I am collecting all the server details from config and trying to connect ONE BY ONE.
public async Task Start()
{
List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
foreach (var serverdetails in servers)
{
var data = Task.Run(() => ConnectToServer(serverdetails ));
}
}
}
Here is my ConnectToServer method
private async void ConnectToGateway(ServerDetails serverdetails )
{
await _aServerGw.connectToServerByName(serverdetails.serveraddress);
}
I have extended the connectToServerByName method as follow , which is in separate static class.
public static class ComLibraryExtensions
{
public static Task connectToServerByName(this ProxyGW #this, string serveraddress)
{
var tcs = new TaskCompletionSource<object>();
Action onSuccess = null;
Action<int> onFailed = null;
onSuccess = () =>
{
#this.onConnectSucceeded -= HandleManager_OnConnectSucceeded;
#this.onConnectFailed -= HandleManager_OnConnectFailed;
tcs.TrySetResult(null);
};
onFailed = hr =>
{
#this.onConnectSucceeded -= HandleManager_OnConnectSucceeded;
#this.onConnectFailed -= HandleManager_OnConnectFailed;
tcs.TrySetException(Marshal.GetExceptionForHR(hr));
};
#this.onConnectSucceeded += HandleManager_OnConnectSucceeded;
#this.onConnectFailed += HandleManager_OnConnectFailed;
#this.connectToGatewayByNameEx(serveraddress);
return tcs.Task;
}
private static void HandleManager_OnConnectFailed(int hr)
{
//How do I get access to dependent objects here?
//Like ILogger ??
_logger.Information(hr);
}
private static void HandleManager_OnConnectSucceeded()
{
//How do I get access #this??
#this.enableNotifications(true);//fails , it says #this does not exists
}
}
Question is:
How do I get access to _aServerGw in HandleManager_OnConnectSucceeded event, because I want to set some property based on the success event.
How do I get access to dependent objects here in extension classes like ILogger?
As a caveat I'm a novice with Rx (2 weeks) and have been experimenting with using Rx, RxUI and Roland Pheasant's DynamicData.
I have a service that initially loads data from local persistence and then, upon some user (or system) instruction will contact the server (TriggerServer in the example) to get additional or replacement data. The solution I've come up with uses a Subject and I've come across many a site discussing the pros/cons of using them. Although I understand the basics of hot/cold it's all based on reading rather than real world.
So, using the below as a simplified version, is this 'right' way of going about this problem or is there something I haven't properly understood somewhere?
NB: I'm not sure how important it is, but the actual code is taken from a Xamarin.Forms app, that uses RxUI, the user input being a ReactiveCommand.
Example:
using DynamicData;
using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
public class MyService : IDisposable
{
private CompositeDisposable _cleanup;
private Subject<Unit> _serverSubject = new Subject<Unit>();
public MyService()
{
var data = Initialise().Publish();
AllData = data.AsObservableCache();
_cleanup = new CompositeDisposable(AllData, data.Connect());
}
public IObservableCache<MyData, Guid> AllData { get; }
public void TriggerServer()
{
// This is what I'm not sure about...
_serverSubject.OnNext(Unit.Default);
}
private IObservable<IChangeSet<MyData, Guid>> Initialise()
{
return ObservableChangeSet.Create<MyData, Guid>(async cache =>
{
// inital load - is this okay?
cache.AddOrUpdate(await LoadLocalData());
// is this a valid way of doing this?
var sync = _serverSubject.Select(_ => GetDataFromServer())
.Subscribe(async task =>
{
var data = await task.ConfigureAwait(false);
cache.AddOrUpdate(data);
});
return new CompositeDisposable(sync);
}, d=> d.Id);
}
private IObservable<MyData> LoadLocalData()
{
return Observable.Timer(TimeSpan.FromSeconds(3)).Select(_ => new MyData("localdata"));
}
private async Task<MyData> GetDataFromServer()
{
await Task.Delay(2000).ConfigureAwait(true);
return new MyData("serverdata");
}
public void Dispose()
{
_cleanup?.Dispose();
}
}
public class MyData
{
public MyData(string value)
{
Value = value;
}
public Guid Id { get; } = Guid.NewGuid();
public string Value { get; set; }
}
And a simple Console app to run:
public static class TestProgram
{
public static void Main()
{
var service = new MyService();
service.AllData.Connect()
.Bind(out var myData)
.Subscribe(_=> Console.WriteLine("data in"), ()=> Console.WriteLine("COMPLETE"));
while (Continue())
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine($"Triggering Server Call, current data is: {string.Join(", ", myData.Select(x=> x.Value))}");
service.TriggerServer();
}
}
private static bool Continue()
{
Console.WriteLine("Press any key to call server, x to exit");
var key = Console.ReadKey();
return key.Key != ConsoleKey.X;
}
}
Looks very good for first try with Rx
I would suggest few changes:
1) Remove the Initialize() call from the constructor and make it a public method - helps a lot with unit tests and now you can await it if you need to
public static void Main()
{
var service = new MyService();
service.Initialize();
2) Add Throttle to you trigger - this fixes parallel calls to the server returning the same results
3) Don't do anything that can throw in Subscribe, use Do instead:
var sync = _serverSubject
.Throttle(Timespan.FromSeconds(0.5), RxApp.TaskPoolScheduler) // you can pass a scheduler via arguments, or use TestScheduler in unit tests to make time pass faster
.Do(async _ =>
{
var data = await GetDataFromServer().ConfigureAwait(false); // I just think this is more readable, your way was also correct
cache.AddOrUpdate(data);
})
// .Retry(); // or anything alese to handle failures
.Subscribe();
I'm putting what I've come to as my solution just in case there's others that find this while they're wandering the internets.
I ended up removing the Subjects all together and chaining together several SourceCache, so when one changed it pushed into the other and so on. I've removed some code for brevity:
public class MyService : IDisposable
{
private SourceCache<MyData, Guid> _localCache = new SourceCache<MyData, Guid>(x=> x.Id);
private SourceCache<MyData, Guid> _serverCache = new SourceCache<MyData, Guid>(x=> x.Id);
public MyService()
{
var localdata = _localCache.Connect();
var serverdata = _serverCache.Connect();
var alldata = localdata.Merge(serverdata);
AllData = alldata.AsObservableCache();
}
public IObservableCache<MyData, Guid> AllData { get; }
public IObservable<Unit> TriggerLocal()
{
return LoadLocalAsync().ToObservable();
}
public IObservable<Unit> TriggerServer()
{
return LoadServerAsync().ToObservable();
}
}
EDIT: I've changed this again to remove any chaining of caches - I just manage the one cache internally. Lesson is not to post too early.
I am building api for one application. what i want is when I get a new order, i have to insert it in DB and I want to rise an event (without blocking my api call may be async). so that latter in other class i can handle that event and do the other work like send the new order notification to account and send order update to user using pubnub.
public class OrderService : IOrderService
{
public delegate void OrderEventHandler(Order order,bool isNewOrder);
public static event OrderEventHandler OrderEvents = delegate { };
public OrderService()
{
OrderEventListener listener = new OrderEventListener();
OrderEvents += new OrderEventHandler(listener.HandleOrderEvents);
}
#region Methods
public void Test()
{
INPRODataFacade facade = new NPRODataFacade();
var unitOfWork = facade.GetUnitOfWork();
var order = unitOfWork.OrderRepository.Find(o => o.OrderID == 1).FirstOrDefault();
unitOfWork.Commit();
facade.ReturnUnitOfWork();
RiseEvent(order,true);
}
private async System.Threading.Tasks.Task RiseEvent(Order order,bool isNewOrder)
{
if (order == null)
{
return;
}
OrderEvents(order,isNewOrder);
}
}
public class OrderEventListener
{
PubNub.PubNub pubNub = null;
const string ChanelPrifix = "fasttract";
public OrderEventListener()
{
pubNub = new PubNub.PubNub(ConfigurationSettings.PubNub_SubscribeKey, ConfigurationSettings.PubNub_PublishKey, ConfigurationSettings.PubNub_SecretKey, ConfigurationSettings.PubNub_SSlOn);
}
public void HandleOrderEvents(Order order, bool isNewOrder)
{
string chanelName = isNewOrder ? string.Format("{0}_{1}", ChanelPrifix, order.AccountID)
: string.Format("{0}_{1}_{2}", ChanelPrifix, order.AccountID, order.OrderID);
var channel = pubNub.Channel(chanelName);
channel.Publish(new PubNubMessageModel { Message = GetMessage( order, isNewOrder) });
}
public string GetMessage(Order order,bool isNewOrder)
{
string message = string.Empty;
if(isNewOrder)
{
message = "You have recieved a new order #" + order.OrderID;
return message;
}
message = order.Status.ToString();
return message;
}
}
Here have noticed that it is blocking my api call. means I am not getting api response back until event handler finished its execution. is this the right way to handle events ? I think there should be a way that i just add events while getting new order and then in listener i process that events anytime without blocking api call.