1
0

complete lab4 again

This commit is contained in:
Rokas Puzonas 2024-12-13 14:20:35 +02:00
parent ac702ce8f3
commit 5c5afc003b
54 changed files with 631 additions and 2852 deletions

View File

@ -1,61 +0,0 @@
//set the language version
syntax = "proto3";
//this will translate into C# namespace
package ChatRoomContract.Protocol;
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
service ChatRoom {
rpc RegisterClient(RegisterClientRequest) returns (ClientId);
rpc GetStrikes(ClientId) returns (Srikes);
rpc GetBlockedUntil(ClientId) returns (BlockedUntil);
rpc SendMessage(UserMessageRequest) returns (BoolResponse);
rpc GetNewMessage(google.protobuf.Empty) returns (NewUserMessage);
rpc RejectMessage(MessageId) returns (google.protobuf.Empty);
rpc ApproveMessage(MessageId) returns (google.protobuf.Empty);
}
message BoolResponse {
bool success = 1;
}
message RegisterClientRequest {
string name = 1;
}
message Srikes {
int32 strikes = 1;
}
message ClientId {
int32 id = 1;
}
message MessageId {
int32 id = 1;
}
message BlockedUntil {
bool hasTimestamp = 1;
google.protobuf.Timestamp timestamp = 2;
}
message UserMessageRequest {
int32 clientId = 1;
string contents = 2;
bool needsToBeCensored = 3;
}
message UserMessage {
int32 id = 1;
string contents = 2;
bool needsToBeCensored = 3;
}
message NewUserMessage {
bool hasMessage = 1;
UserMessage message = 2;
}

View File

@ -1,966 +0,0 @@
//----------------------
// <auto-generated>
// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
#pragma warning disable 612 // Disable "CS0612 '...' is obsolete"
#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null"
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
#pragma warning disable 8603 // Disable "CS8603 Possible null reference return"
#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter"
#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type"
#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)."
namespace NswagGenerated
{
using System = global::System;
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class NswagChatRoomClient
{
#pragma warning disable 8618
private string _baseUrl;
#pragma warning restore 8618
private System.Net.Http.HttpClient _httpClient;
private static System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings, true);
private Newtonsoft.Json.JsonSerializerSettings _instanceSettings;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public NswagChatRoomClient(string baseUrl, System.Net.Http.HttpClient httpClient)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
BaseUrl = baseUrl;
_httpClient = httpClient;
Initialize();
}
private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
}
public string BaseUrl
{
get { return _baseUrl; }
set
{
_baseUrl = value;
if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/"))
_baseUrl += '/';
}
}
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } }
static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
partial void Initialize();
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
/// <summary>
/// Register client with a name
/// </summary>
/// <param name="name">Name of client, can be duplicate between clients</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<int> RegisterClientAsync(string name)
{
return RegisterClientAsync(name, System.Threading.CancellationToken.None);
}
/// <summary>
/// Register client with a name
/// </summary>
/// <param name="name">Name of client, can be duplicate between clients</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual int RegisterClient(string name)
{
return System.Threading.Tasks.Task.Run(async () => await RegisterClientAsync(name, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Register client with a name
/// </summary>
/// <param name="name">Name of client, can be duplicate between clients</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<int> RegisterClientAsync(string name, System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "text/plain");
request_.Method = new System.Net.Http.HttpMethod("POST");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "registerClient"
urlBuilder_.Append("registerClient");
urlBuilder_.Append('?');
if (name != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("name")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(name, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<int>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
/// <summary>
/// Get number of strikes a participant has
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<int> GetStrikesAsync(int? clientId)
{
return GetStrikesAsync(clientId, System.Threading.CancellationToken.None);
}
/// <summary>
/// Get number of strikes a participant has
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual int GetStrikes(int? clientId)
{
return System.Threading.Tasks.Task.Run(async () => await GetStrikesAsync(clientId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Get number of strikes a participant has
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<int> GetStrikesAsync(int? clientId, System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "getStrikes"
urlBuilder_.Append("getStrikes");
urlBuilder_.Append('?');
if (clientId != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("clientId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<int>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
/// <summary>
/// Get timestamp until when the client is blocked
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<System.DateTimeOffset> GetBlockedUntilAsync(int? clientId)
{
return GetBlockedUntilAsync(clientId, System.Threading.CancellationToken.None);
}
/// <summary>
/// Get timestamp until when the client is blocked
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.DateTimeOffset GetBlockedUntil(int? clientId)
{
return System.Threading.Tasks.Task.Run(async () => await GetBlockedUntilAsync(clientId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Get timestamp until when the client is blocked
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<System.DateTimeOffset> GetBlockedUntilAsync(int? clientId, System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "getBlockedUntil"
urlBuilder_.Append("getBlockedUntil");
urlBuilder_.Append('?');
if (clientId != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("clientId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<System.DateTimeOffset>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
/// <summary>
/// Send a message, will be given to a moderator to be approved
/// </summary>
/// <param name="clientId">Client ID</param>
/// <param name="contents">Message contents</param>
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<bool> SendMessageAsync(int? clientId, string contents, bool? needsToBeCensored)
{
return SendMessageAsync(clientId, contents, needsToBeCensored, System.Threading.CancellationToken.None);
}
/// <summary>
/// Send a message, will be given to a moderator to be approved
/// </summary>
/// <param name="clientId">Client ID</param>
/// <param name="contents">Message contents</param>
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual bool SendMessage(int? clientId, string contents, bool? needsToBeCensored)
{
return System.Threading.Tasks.Task.Run(async () => await SendMessageAsync(clientId, contents, needsToBeCensored, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Send a message, will be given to a moderator to be approved
/// </summary>
/// <param name="clientId">Client ID</param>
/// <param name="contents">Message contents</param>
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<bool> SendMessageAsync(int? clientId, string contents, bool? needsToBeCensored, System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "text/plain");
request_.Method = new System.Net.Http.HttpMethod("POST");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "sendMessage"
urlBuilder_.Append("sendMessage");
urlBuilder_.Append('?');
if (clientId != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("clientId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
if (contents != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("contents")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(contents, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
if (needsToBeCensored != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("needsToBeCensored")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(needsToBeCensored, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<bool>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
/// <summary>
/// Get the next message which hasn't been approved or rejected
/// </summary>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<Message> GetNewMessageAsync()
{
return GetNewMessageAsync(System.Threading.CancellationToken.None);
}
/// <summary>
/// Get the next message which hasn't been approved or rejected
/// </summary>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual Message GetNewMessage()
{
return System.Threading.Tasks.Task.Run(async () => await GetNewMessageAsync(System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Get the next message which hasn't been approved or rejected
/// </summary>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<Message> GetNewMessageAsync(System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "getNewMessage"
urlBuilder_.Append("getNewMessage");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<Message>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
/// <summary>
/// Reject a message
/// </summary>
/// <param name="messageId">Message ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task RejectMessageAsync(int? messageId)
{
return RejectMessageAsync(messageId, System.Threading.CancellationToken.None);
}
/// <summary>
/// Reject a message
/// </summary>
/// <param name="messageId">Message ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual void RejectMessage(int? messageId)
{
System.Threading.Tasks.Task.Run(async () => await RejectMessageAsync(messageId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Reject a message
/// </summary>
/// <param name="messageId">Message ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task RejectMessageAsync(int? messageId, System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json");
request_.Method = new System.Net.Http.HttpMethod("POST");
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "rejectMessage"
urlBuilder_.Append("rejectMessage");
urlBuilder_.Append('?');
if (messageId != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("messageId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(messageId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
/// <summary>
/// Approve a message
/// </summary>
/// <param name="messageId">Message ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task ApproveMessageAsync(int? messageId)
{
return ApproveMessageAsync(messageId, System.Threading.CancellationToken.None);
}
/// <summary>
/// Approve a message
/// </summary>
/// <param name="messageId">Message ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual void ApproveMessage(int? messageId)
{
System.Threading.Tasks.Task.Run(async () => await ApproveMessageAsync(messageId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <summary>
/// Approve a message
/// </summary>
/// <param name="messageId">Message ID</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task ApproveMessageAsync(int? messageId, System.Threading.CancellationToken cancellationToken)
{
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json");
request_.Method = new System.Net.Http.HttpMethod("POST");
var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "approveMessage"
urlBuilder_.Append("approveMessage");
urlBuilder_.Append('?');
if (messageId != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("messageId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(messageId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
{
this.Object = responseObject;
this.Text = responseText;
}
public T Object { get; }
public string Text { get; }
}
public bool ReadResponseAsString { get; set; }
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
{
if (response == null || response.Content == null)
{
return new ObjectResponseResult<T>(default(T), string.Empty);
}
if (ReadResponseAsString)
{
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
return new ObjectResponseResult<T>(typedBody, responseText);
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
}
}
else
{
try
{
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var streamReader = new System.IO.StreamReader(responseStream))
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
{
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
var typedBody = serializer.Deserialize<T>(jsonTextReader);
return new ObjectResponseResult<T>(typedBody, string.Empty);
}
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
}
}
}
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
{
return "";
}
if (value is System.Enum)
{
var name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value != null ? attribute.Value : name;
}
}
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[]) value);
}
else if (value is string[])
{
return string.Join(",", (string[])value);
}
else if (value.GetType().IsArray)
{
var valueArray = (System.Array)value;
var valueTextArray = new string[valueArray.Length];
for (var i = 0; i < valueArray.Length; i++)
{
valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo);
}
return string.Join(",", valueTextArray);
}
var result = System.Convert.ToString(value, cultureInfo);
return result == null ? "" : result;
}
}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class Message
{
[Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public int Id { get; set; }
[Newtonsoft.Json.JsonProperty("contents", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Contents { get; set; }
[Newtonsoft.Json.JsonProperty("needsToBeCensored", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public bool NeedsToBeCensored { get; set; }
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class ApiException : System.Exception
{
public int StatusCode { get; private set; }
public string Response { get; private set; }
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Exception innerException)
: base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException)
{
StatusCode = statusCode;
Response = response;
Headers = headers;
}
public override string ToString()
{
return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
}
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class ApiException<TResult> : ApiException
{
public TResult Result { get; private set; }
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, TResult result, System.Exception innerException)
: base(message, statusCode, response, headers, innerException)
{
Result = result;
}
}
}
#pragma warning restore 108
#pragma warning restore 114
#pragma warning restore 472
#pragma warning restore 612
#pragma warning restore 1573
#pragma warning restore 1591
#pragma warning restore 8073
#pragma warning restore 3016
#pragma warning restore 8603
#pragma warning restore 8604
#pragma warning restore 8625

View File

@ -1,19 +0,0 @@
{
"runtime": "Net60",
"documentGenerator": {
"fromDocument": {
"url": "http://localhost:5000/swagger/v1/swagger.json",
"output": null
}
},
"codeGenerators": {
"openApiToCSharpClient": {
"classname" : "NswagChatRoomClient",
"namespace" : "NswagGenerated",
"output" : "NswagChatRoomClient.cs",
"generateSyncMethods": true
}
}
}

View File

@ -2,35 +2,13 @@
"version": "0.2.0",
"configurations": [
{
"name": "ChatRoom",
"name": "ChatRoomSimpleRPC",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ChatRoom",
"program": "${workspaceFolder}/ChatRoom/bin/Debug/net6.0/ChatRoom.dll",
"preLaunchTask": "build-ChatRoomSimpleRPC",
"program": "${workspaceFolder}/ChatRoomSimpleRPC/bin/Debug/net6.0/ChatRoomSimpleRPC.dll",
"args": [],
"cwd": "${workspaceFolder}/ChatRoom",
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": "ModeratorGrpc",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ModeratorGrpc",
"program": "${workspaceFolder}/ModeratorGrpc/bin/Debug/net6.0/ModeratorGrpc.dll",
"args": [],
"cwd": "${workspaceFolder}/ModeratorGrpc",
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": "ParticipantGrpc",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ParticipantGrpc",
"program": "${workspaceFolder}/ParticipantGrpc/bin/Debug/net6.0/ParticipantGrpc.dll",
"args": [],
"cwd": "${workspaceFolder}/ParticipantGrpc",
"cwd": "${workspaceFolder}/ChatRoomSimpleRPC",
"console": "externalTerminal",
"stopAtEntry": false
},
@ -46,57 +24,35 @@
"stopAtEntry": false
},
{
"name": "ParticipantRest",
"name": "ParticipantGrpc",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ParticipantRest",
"program": "${workspaceFolder}/ParticipantRest/bin/Debug/net6.0/ParticipantRest.dll",
"preLaunchTask": "build-ParticipantGrpc",
"program": "${workspaceFolder}/ParticipantGrpc/bin/Debug/net6.0/ParticipantGrpc.dll",
"args": [],
"cwd": "${workspaceFolder}/ParticipantRest",
"cwd": "${workspaceFolder}/ParticipantGrpc",
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": "ModeratorSimpleRPC",
"name": "RestToSimpleRPCAdapter",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ModeratorSimpleRPC",
"program": "${workspaceFolder}/ModeratorSimpleRPC/bin/Debug/net6.0/ModeratorSimpleRPC.dll",
"preLaunchTask": "build-RestToSimpleRPCAdapter",
"program": "${workspaceFolder}/RestToSimpleRPCAdapter/bin/Debug/net6.0/RestToSimpleRPCAdapter.dll",
"args": [],
"cwd": "${workspaceFolder}/ModeratorSimpleRPC",
"cwd": "${workspaceFolder}/RestToSimpleRPCAdapter",
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": "ParticipantSimpleRPC",
"name": "GrpcToSimpleRPCAdapter",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ParticipantSimpleRPC",
"program": "${workspaceFolder}/ParticipantSimpleRPC/bin/Debug/net6.0/ParticipantSimpleRPC.dll",
"preLaunchTask": "build-GrpcToSimpleRPCAdapter",
"program": "${workspaceFolder}/GrpcToSimpleRPCAdapter/bin/Debug/net6.0/GrpcToSimpleRPCAdapter.dll",
"args": [],
"cwd": "${workspaceFolder}/ParticipantSimpleRPC",
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": "ModeratorRabbitMQ",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ModeratorRabbitMQ",
"program": "${workspaceFolder}/ModeratorRabbitMQ/bin/Debug/net6.0/ModeratorRabbitMQ.dll",
"args": [],
"cwd": "${workspaceFolder}/ModeratorRabbitMQ",
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": "ParticipantRabbitMQ",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-ParticipantRabbitMQ",
"program": "${workspaceFolder}/ParticipantRabbitMQ/bin/Debug/net6.0/ParticipantRabbitMQ.dll",
"args": [],
"cwd": "${workspaceFolder}/ParticipantRabbitMQ",
"cwd": "${workspaceFolder}/GrpcToSimpleRPCAdapter",
"console": "externalTerminal",
"stopAtEntry": false
}

View File

@ -2,19 +2,19 @@
"version": "2.0.0",
"tasks": [
{
"label": "build-ChatRoom",
"label": "build-ChatRoomSimpleRPC",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ChatRoom/ChatRoom.csproj",
"${workspaceFolder}/ChatRoomSimpleRPC/ChatRoomSimpleRPC.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ModeratorGrpc",
"label": "build-ModeratorRest",
"command": "dotnet",
"type": "process",
"args": [
@ -25,30 +25,6 @@
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ParticipantRest",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ParticipantRest/ParticipantRest.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ModeratorGrpc",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ModeratorGrpc/ModeratorGrpc.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ParticipantGrpc",
"command": "dotnet",
@ -62,48 +38,24 @@
"problemMatcher": "$msCompile"
},
{
"label": "build-ModeratorSimpleRPC",
"label": "build-RestToSimpleRPCAdapter",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ModeratorSimpleRPC/ModeratorSimpleRPC.csproj",
"${workspaceFolder}/RestToSimpleRPCAdapter/RestToSimpleRPCAdapter.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ParticipantSimpleRPC",
"label": "build-GrpcToSimpleRPCAdapter",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ParticipantSimpleRPC/ParticipantSimpleRPC.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ModeratorRabbitMQ",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ModeratorRabbitMQ/ModeratorRabbitMQ.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-ParticipantRabbitMQ",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/ParticipantRabbitMQ/ParticipantRabbitMQ.csproj",
"${workspaceFolder}/GrpcToSimpleRPCAdapter/GrpcToSimpleRPCAdapter.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],

View File

@ -1,159 +0,0 @@
using RabbitMQ.Client;
using Newtonsoft.Json;
using RabbitMQ.Client.Events;
using ChatRoomContract;
using System.Text;
using MessagePack;
using System.Diagnostics;
using NLog.LayoutRenderers.Wrappers;
using NLog;
namespace ChatRoom;
internal class ChatRoomRabbitMQService
{
/// <summary>
/// Logger for this class.
/// </summary>
private Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Communications channel to RabbitMQ message broker.
/// </summary>
private IModel rmqChannel;
/// <summary>
/// Service logic.
/// </summary>
private ChatRoomLogic logic;
public ChatRoomRabbitMQService(ChatRoomLogic logic, IConnection connection, string exchangeName, string serverQueueName)
{
this.logic = logic;
//get channel, configure exchanges and request queue
rmqChannel = connection.CreateModel();
rmqChannel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct);
rmqChannel.QueueDeclare(queue: serverQueueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
rmqChannel.QueueBind(queue: serverQueueName, exchange: exchangeName, routingKey: serverQueueName, arguments: null);
//connect to the queue as consumer
//XXX: see https://www.rabbitmq.com/dotnet-api-guide.html#concurrency for threading issues
var rmqConsumer = new EventingBasicConsumer(rmqChannel);
rmqConsumer.Received += (consumer, delivery) => OnMessageReceived(((EventingBasicConsumer)consumer).Model, delivery);
rmqChannel.BasicConsume(queue: serverQueueName, autoAck: true, consumer: rmqConsumer);
}
/// <summary>
/// Is invoked to process messages received.
/// </summary>
/// <param name="channel">Related communications channel.</param>
/// <param name="msgIn">Message deliver data.</param>
private void OnMessageReceived(IModel channel, BasicDeliverEventArgs msgIn)
{
try
{
var msg = MessagePackSerializer.Deserialize<RPCMessage>(msgIn.Body);
Debug.Assert(msg != null);
if (msg.isResponse == true)
{
return;
}
bool hasResponse = false;
byte[]? response = null;
switch (msg.method)
{
case nameof(IChatRoomService.RegisterClient):
{
var name = MessagePackSerializer.Deserialize<string>(msg.args);
var clientId = logic.RegisterClient(name);
response = MessagePackSerializer.Serialize(clientId);
hasResponse = true;
break;
}
case nameof(IChatRoomService.GetStrikes):
{
var clientId = MessagePackSerializer.Deserialize<int>(msg.args);
var strikes = logic.GetStrikes(clientId);
response = MessagePackSerializer.Serialize(strikes);
hasResponse = true;
break;
}
case nameof(IChatRoomService.GetBlockedUntil):
{
var clientId = MessagePackSerializer.Deserialize<int>(msg.args);
var blockedUntil = logic.GetBlockedUntil(clientId);
response = MessagePackSerializer.Serialize(blockedUntil);
hasResponse = true;
break;
}
case nameof(IChatRoomService.SendMessage):
{
var args = MessagePackSerializer.Deserialize<SendMessageArgs>(msg.args);
var success = logic.SendMessage(args.clientId, args.contents, args.needsToBeCensored);
response = MessagePackSerializer.Serialize(success);
hasResponse = true;
break;
}
case nameof(IChatRoomService.GetNewMessage):
{
var newMessage = logic.GetNewMessage();
response = MessagePackSerializer.Serialize(newMessage);
hasResponse = true;
break;
}
case nameof(IChatRoomService.RejectMessage):
{
var messageId = MessagePackSerializer.Deserialize<int>(msg.args);
logic.ApproveMessage(messageId);
break;
}
case nameof(IChatRoomService.ApproveMessage):
{
var messageId = MessagePackSerializer.Deserialize<int>(msg.args);
logic.ApproveMessage(messageId);
break;
}
default:
{
throw new Exception("Unknown RPC method");
}
}
if (hasResponse)
{
var responseMsg = new RPCMessage
{
isResponse = true,
method = msg.method,
args = response
};
var properties = channel.CreateBasicProperties();
properties.CorrelationId = msgIn.BasicProperties.CorrelationId;
channel.BasicPublish(
exchange: msgIn.Exchange,
routingKey: msgIn.BasicProperties.ReplyTo,
basicProperties: properties,
body: MessagePackSerializer.Serialize(responseMsg)
);
}
}
catch (Exception e)
{
log.Error(e, "Unhandled exception caught when processing a message. The message is now lost.");
}
}
}

View File

@ -1,197 +0,0 @@
using System.Net;
using NLog;
using SimpleRpc.Transports;
using SimpleRpc.Transports.Http.Server;
using SimpleRpc.Serialization.Hyperion;
using ChatRoomContract;
using Microsoft.Extensions.Logging;
using System.Reflection;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Server.Kestrel.Core;
namespace ChatRoom;
public class Server
{
/// <summary>
/// Logger for this class.
/// </summary>
Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Configure loggin subsystem.
/// </summary>
private void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
/// <summary>
/// Program entry point.
/// </summary>
/// <param name="args">Command line arguments.</param>
public static void Main(string[] args)
{
var self = new Server();
self.Run(args);
}
/// <summary>
/// Program body.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void Run(string[] args)
{
//configure logging
ConfigureLogging();
//indicate server is about to start
log.Info("Server is about to start");
//start the server
StartServer(args);
}
/// <summary>
/// Starts integrated server.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void StartServer(string[] args)
{
Console.Title = "Chat Room";
var logic = new ChatRoomLogic();
// Simple RPC
WebApplication simpleRPCWebApplication;
{
///create web app builder
var builder = WebApplication.CreateBuilder(args);
//configure integrated server
builder.WebHost.ConfigureKestrel(opts => {
opts.Listen(IPAddress.Loopback, 5001);
});
//add SimpleRPC services
builder.Services
.AddSimpleRpcServer(new HttpServerTransportOptions { Path = "/simplerpc" })
.AddSimpleRpcHyperionSerializer();
//add our custom services
builder.Services.AddSingleton<IChatRoomService>(new ChatRoomSimpleRPCService(logic));
//build the server
var app = builder.Build();
//add SimpleRPC middleware
app.UseSimpleRpcServer();
simpleRPCWebApplication = app;
}
// Rest, GRPC
WebApplication restGrpcWebApplication;
{
///create web app builder
var builder = WebApplication.CreateBuilder(args);
//configure integrated server
builder.WebHost.ConfigureKestrel(opts => {
opts.Listen(IPAddress.Loopback, 5000, opts =>
{
opts.Protocols = HttpProtocols.Http2;
});
});
//add and configure swagger documentation generator (http://127.0.0.1:5000/swagger/)
builder.Services.AddSwaggerGen(opts =>
{
//include code comments in swagger documentation
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
opts.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
//turn on support for web api controllers
builder.Services
.AddControllers()
.AddJsonOptions(opts =>
{
//this makes enumeration values to be strings instead of integers in opeanapi doc
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
//add CORS policies
builder.Services.AddCors(cr =>
{
//allow everything from everywhere
cr.AddPolicy("allowAll", cp =>
{
cp.AllowAnyOrigin();
cp.AllowAnyMethod();
cp.AllowAnyHeader();
});
});
//publish the background logic as (an internal) service through dependency injection,
//otherwise it will not start until the first client calls into controller
builder.Services.AddSingleton(logic);
//add support for GRPC services
builder.Services.AddGrpc();
builder.Services.AddSingleton(new ChatRoomGrpcService(logic));
//build the server
var app = builder.Build();
//turn CORS policy on
app.UseCors("allowAll");
//turn on support for swagger doc web page
app.UseSwagger();
app.UseSwaggerUI();
app.MapGrpcService<ChatRoomGrpcService>();
////turn on request routing
app.UseRouting();
////configure routes
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}"
);
restGrpcWebApplication = app;
}
// RabbitMQ
{
var service = new ChatRoomRabbitMQService(
logic,
RabbitMQConfig.CreateConnection(),
RabbitMQConfig.ExchangeName,
RabbitMQConfig.ServerQueueName
);
}
var restGrpcTask = restGrpcWebApplication.RunAsync();
var simpleRPCTask = simpleRPCWebApplication.RunAsync();
restGrpcTask.Wait();
simpleRPCTask.Wait();
}
}

View File

@ -11,32 +11,17 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
<PackageReference Include="MessagePack" Version="3.0.300" />
<PackageReference Include="Grpc.AspNetCore" Version="2.49.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NSwag.MSBuild" Version="14.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\service.proto" />
</ItemGroup>
<!-- This will only work if server is running. It needs access to http://localhost:5000/swagger/v1/swagger.json . -->
<Target Name="NSwag" BeforeTargets="BeforeBuild">
<Exec
Command="$(NSwagExe_Net60) run nswag.json /variables:Configuration=$(Configuration),OutDir=$(OutDir)"
IgnoreExitCode="true"
/>
<!-- See 'https://stackoverflow.com/questions/44818730/is-there-a-net-core-cli-pre-before-build-task' as to why the following is necessary-->
<ItemGroup>
<Compile
Include="**/*$(DefaultLanguageSourceExtension)"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(BaseIntermediateOutputPath)**;$(BaseOutputPath)**;@(Compile)" />
</ItemGroup>
</Target>
</Project>

View File

@ -1,116 +0,0 @@

using ChatRoomContract.Protocol;
using Google.Protobuf.WellKnownTypes;
using Grpc.Net.Client;
namespace ChatRoomContract;
public class ChatRoomGrpcClient : IChatRoomService
{
GrpcChannel channel;
ChatRoom.ChatRoomClient client;
public ChatRoomGrpcClient(string address)
{
channel = GrpcChannel.ForAddress(address);
client = new ChatRoom.ChatRoomClient(channel);
}
/// <summary>
/// Approve a message
/// </summary>
/// <param name="messageId">Message ID</param>
public void ApproveMessage(int messageId)
{
client.ApproveMessage(new MessageId { Id = messageId });
}
/// <summary>
/// Get timestamp until when the client is blocked
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Optional datetime object</returns>
public DateTime? GetBlockedUntil(int clientId)
{
var result = client.GetBlockedUntil(new ClientId { Id = clientId });
if (result.HasTimestamp)
{
return result.Timestamp.ToDateTime();
}
else
{
return null;
}
}
/// <summary>
/// Get the next message which hasn't been approved or rejected
/// </summary>
/// <returns>Message object. Returns null if there is no message</returns>
public Message? GetNewMessage()
{
var result = client.GetNewMessage(new Empty());
if (result.HasMessage)
{
return new Message
{
id = result.Message.Id,
contents = result.Message.Contents,
needsToBeCensored = result.Message.NeedsToBeCensored
};
}
else
{
return null;
}
}
/// <summary>
/// Get number of strikes a participant has
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Number of strikes</returns>
public int GetStrikes(int clientId)
{
var result = client.GetStrikes(new ClientId { Id = clientId });
return result.Strikes;
}
/// <summary>
/// Register client with a name
/// </summary>
/// <param name="name">Name of client, can be duplicate between clients</param>
/// <returns>Client ID</returns>
public int RegisterClient(string name)
{
var result = client.RegisterClient(new RegisterClientRequest { Name = name });
return result.Id;
}
/// <summary>
/// Reject a message
/// </summary>
/// <param name="messageId">Message ID</param>
public void RejectMessage(int messageId)
{
client.RejectMessage(new MessageId { Id = messageId });
}
/// <summary>
/// Send a message, will be given to a moderator to be approved
/// </summary>
/// <param name="clientId">Client ID</param>
/// <param name="contents">Message contents</param>
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
/// <returns>Was sending successful, can fail if user is blocked</returns>
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
{
var result = client.SendMessage(new UserMessageRequest
{
ClientId = clientId,
Contents = contents,
NeedsToBeCensored = needsToBeCensored
});
return result.Success;
}
}

View File

@ -1,243 +0,0 @@
using MessagePack;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace ChatRoomContract;
/// <summary>
/// RabbitMQ chat room client
/// </summary>
public class ChatRoomRabbitMQClient : IChatRoomService
{
/// <summary>
/// Exchange name
/// </summary>
private string exchangeName;
/// <summary>
/// Client queue name
/// </summary>
private string clientQueueName;
/// <summary>
/// Server queue name
/// </summary>
private string serverQueueName;
/// <summary>
/// RabbitMQ channel
/// </summary>
private IModel rmqChannel;
/// <summary>
/// Chat Room client constructor
/// </summary>
/// <param name="connection">RabbitMQ connection</param>
/// <param name="exchangeName">Exchange name</param>
/// <param name="clientQueueName">Client queue name</param>
/// <param name="serverQueueName">Server queue name</param>
public ChatRoomRabbitMQClient(IConnection connection, string exchangeName, string clientQueueName, string serverQueueName)
{
this.exchangeName = exchangeName;
this.clientQueueName = clientQueueName;
this.serverQueueName = serverQueueName;
rmqChannel = connection.CreateModel();
rmqChannel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct);
rmqChannel.QueueDeclare(queue: clientQueueName, durable: false, exclusive: true, autoDelete: false, arguments: null);
rmqChannel.QueueBind(queue: clientQueueName, exchange: exchangeName, routingKey: clientQueueName, arguments: null);
}
/// <summary>
/// Send rpc message without waiting for a response
/// </summary>
/// <param name="method">Method name</param>
/// <param name="args">Serialized arguments</param>
/// <param name="correlationId">Optional correlation ID</param>
private void CallVoid(string method, byte[] args, string? correlationId = null)
{
correlationId ??= Guid.NewGuid().ToString();
var requestProps = rmqChannel.CreateBasicProperties();
requestProps.CorrelationId = correlationId;
requestProps.ReplyTo = clientQueueName;
var msg = new RPCMessage
{
isResponse = false,
method = method,
args = args
};
rmqChannel.BasicPublish(
exchange: exchangeName,
routingKey: serverQueueName,
basicProperties: requestProps,
body: MessagePackSerializer.Serialize(msg)
);
}
/// <summary>
/// Send a rpc message and wait for a response
/// </summary>
/// <typeparam name="ResultType">Result type</typeparam>
/// <param name="method">Method name</param>
/// <param name="args">Serialized arguments</param>
/// <returns>Result</returns>
private ResultType Call<ResultType>(string method, byte[]? args)
{
var correlationId = Guid.NewGuid().ToString();
var isResultReady = false;
var resultReadySignal = new AutoResetEvent(false);
ResultType result = default;
//ensure contents of variables set in main thread, are loadable by receiver thread
Thread.MemoryBarrier();
//create response message consumer
var consumer = new EventingBasicConsumer(rmqChannel);
consumer.Received +=
(channel, delivery) => {
//ensure contents of variables set in main thread are loaded into this thread
Thread.MemoryBarrier();
if (isResultReady)
{
return;
}
if (delivery.BasicProperties.CorrelationId != correlationId)
{
return;
}
var msg = MessagePackSerializer.Deserialize<RPCMessage>(delivery.Body);
if (msg.isResponse && msg.method == method)
{
if (msg.args != null)
{
result = MessagePackSerializer.Deserialize<ResultType>(msg.args);
}
//indicate result has been received, ensure it is loadable by main thread
isResultReady = true;
Thread.MemoryBarrier();
//signal main thread that result has been received
resultReadySignal.Set();
}
};
//attach message consumer to the response queue
var consumerTag = rmqChannel.BasicConsume(clientQueueName, true, consumer);
CallVoid(method, args, correlationId);
//wait for the result to be ready
resultReadySignal.WaitOne();
//ensure contents of variables set by the receiver are loaded into this thread
Thread.MemoryBarrier();
//detach message consumer from the response queue
rmqChannel.BasicCancel(consumerTag);
return result;
}
/// <summary>
/// Approve a message
/// </summary>
/// <param name="messageId">Message ID</param>
public void ApproveMessage(int messageId)
{
CallVoid(
nameof(IChatRoomService.ApproveMessage),
MessagePackSerializer.Serialize(messageId)
);
}
/// <summary>
/// Get timestamp until when the client is blocked
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Optional datetime object</returns>
public DateTime? GetBlockedUntil(int clientId)
{
return Call<DateTime?>(
nameof(IChatRoomService.GetBlockedUntil),
MessagePackSerializer.Serialize(clientId)
);
}
/// <summary>
/// Get the next message which hasn't been approved or rejected
/// </summary>
/// <returns>Message object. Returns null if there is no message</returns>
public Message? GetNewMessage()
{
return Call<Message?>(
nameof(IChatRoomService.GetNewMessage),
null
);
}
/// <summary>
/// Get number of strikes a participant has
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Number of strikes</returns>
public int GetStrikes(int clientId)
{
return Call<int>(
nameof(IChatRoomService.GetStrikes),
MessagePackSerializer.Serialize(clientId)
);
}
/// <summary>
/// Register client with a name
/// </summary>
/// <param name="name">Name of client, can be duplicate between clients</param>
/// <returns>Client ID</returns>
public int RegisterClient(string name)
{
return Call<int>(
nameof(IChatRoomService.RegisterClient),
MessagePackSerializer.Serialize(name)
);
}
/// <summary>
/// Reject a message
/// </summary>
/// <param name="messageId">Message ID</param>
public void RejectMessage(int messageId)
{
CallVoid(
nameof(IChatRoomService.RejectMessage),
MessagePackSerializer.Serialize(messageId)
);
}
/// <summary>
/// Send a message, will be given to a moderator to be approved
/// </summary>
/// <param name="clientId">Client ID</param>
/// <param name="contents">Message contents</param>
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
/// <returns>Was sending successful, can fail if user is blocked</returns>
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
{
var args = new SendMessageArgs
{
clientId = clientId,
contents = contents,
needsToBeCensored = needsToBeCensored
};
return Call<bool>(
nameof(IChatRoomService.SendMessage),
MessagePackSerializer.Serialize(args)
);
}
}

View File

@ -1,124 +0,0 @@

using NswagGenerated;
namespace ChatRoomContract;
/// <summary>
/// Wrapper around generated NSwag client
/// </summary>
public class ChatRoomRestClient : IChatRoomService
{
NswagGenerated.NswagChatRoomClient nswag;
/// <summary>
/// Chat room client constructor
/// </summary>
/// <param name="baseUrl">Server base url</param>
/// <param name="httpClient">HTTP Client</param>
public ChatRoomRestClient(string baseUrl, HttpClient httpClient)
{
nswag = new NswagGenerated.NswagChatRoomClient(baseUrl, httpClient);
}
/// <summary>
/// Approve a message
/// </summary>
/// <param name="messageId">Message ID</param>
public void ApproveMessage(int messageId)
{
nswag.ApproveMessage(messageId);
}
/// <summary>
/// Get timestamp until when the client is blocked
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Optional datetime object</returns>
public DateTime? GetBlockedUntil(int clientId)
{
try
{
var offset = nswag.GetBlockedUntil(clientId);
return offset.DateTime;
}
catch (ApiException e)
{
if (e.StatusCode == 204)
{
return null;
}
throw;
}
}
/// <summary>
/// Get the next message which hasn't been approved or rejected
/// </summary>
/// <returns>Message object. Returns null if there is no message</returns>
public Message? GetNewMessage()
{
NswagGenerated.Message message;
try
{
message = nswag.GetNewMessage();
}
catch (ApiException e)
{
if (e.StatusCode == 204)
{
return null;
}
throw;
}
return new Message
{
id = message.Id,
contents = message.Contents,
needsToBeCensored = message.NeedsToBeCensored
};
}
/// <summary>
/// Get number of strikes a participant has
/// </summary>
/// <param name="clientId">Client ID</param>
/// <returns>Number of strikes</returns>
public int GetStrikes(int clientId)
{
return nswag.GetStrikes(clientId);
}
/// <summary>
/// Register client with a name
/// </summary>
/// <param name="name">Name of client, can be duplicate between clients</param>
/// <returns>Client ID</returns>
public int RegisterClient(string name)
{
return nswag.RegisterClient(name);
}
/// <summary>
/// Reject a message
/// </summary>
/// <param name="messageId">Message ID</param>
public void RejectMessage(int messageId)
{
nswag.RejectMessage(messageId);
}
/// <summary>
/// Send a message, will be given to a moderator to be approved
/// </summary>
/// <param name="clientId">Client ID</param>
/// <param name="contents">Message contents</param>
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
/// <returns>Was sending successful, can fail if user is blocked</returns>
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
{
return nswag.SendMessage(clientId, contents, needsToBeCensored);
}
}

View File

@ -5,12 +5,12 @@ using Grpc.Net.Client;
namespace ChatRoomContract;
public class ChatRoomClient : IChatRoomService
public class GrpcChatRoomClient : IChatRoomService
{
GrpcChannel channel;
ChatRoom.ChatRoomClient client;
public ChatRoomClient(string address)
public GrpcChatRoomClient(string address)
{
channel = GrpcChannel.ForAddress(address);
client = new ChatRoom.ChatRoomClient(channel);

View File

@ -1,26 +1,21 @@
using MessagePack;
namespace ChatRoomContract;
namespace ChatRoomContract;
/// <summary>
/// Minimal message description
/// </summary>
[MessagePackObject]
public class Message
{
/// <summary>
/// Message ID
/// </summary>
[Key(0)]
public int id;
/// <summary>
/// Message contents
/// </summary>
[Key(1)]
public string contents;
/// <summary>
/// Does this message need to be censored?
/// </summary>
[Key(2)]
public bool needsToBeCensored;
}

View File

@ -1,4 +1,4 @@
//----------------------
//----------------------
// <auto-generated>
// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
// </auto-generated>
@ -23,19 +23,19 @@ namespace NswagGenerated
using System = global::System;
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class NswagChatRoomClient
public partial class NswagChatRoomClient
{
#pragma warning disable 8618
#pragma warning disable 8618
private string _baseUrl;
#pragma warning restore 8618
#pragma warning restore 8618
private System.Net.Http.HttpClient _httpClient;
private static System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings, true);
private Newtonsoft.Json.JsonSerializerSettings _instanceSettings;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public NswagChatRoomClient(string baseUrl, System.Net.Http.HttpClient httpClient)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
BaseUrl = baseUrl;
_httpClient = httpClient;
@ -860,7 +860,7 @@ namespace NswagGenerated
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
@ -872,13 +872,13 @@ namespace NswagGenerated
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[])value);
return System.Convert.ToBase64String((byte[]) value);
}
else if (value is string[])
{
@ -953,10 +953,10 @@ namespace NswagGenerated
}
#pragma warning restore 108
#pragma warning restore 114
#pragma warning restore 472
#pragma warning restore 612
#pragma warning restore 108
#pragma warning restore 114
#pragma warning restore 472
#pragma warning restore 612
#pragma warning restore 1573
#pragma warning restore 1591
#pragma warning restore 8073

View File

@ -1,49 +0,0 @@
using MessagePack;
namespace ChatRoomContract;
/// <summary>
/// RabbitMQ message
/// </summary>
[MessagePackObject]
public class RPCMessage
{
/// <summary>
/// Is this message a response
/// </summary>
[Key(0)]
public bool isResponse;
/// <summary>
/// Method name
/// </summary>
[Key(1)]
public string method;
/// <summary>
/// Optional arguments
/// </summary>
[Key(2)]
public byte[]? args;
}
/// <summary>
/// IChatRoomService.SendMessage arguments
/// </summary>
[MessagePackObject]
public class SendMessageArgs
{
/// <summary>
/// Client ID
/// </summary>
[Key(0)]
public int clientId { get; set; }
/// <summary>
/// Message contents
/// </summary>
[Key(1)]
public string contents { get; set; }
/// <summary>
/// Does this message need to be censored?
/// </summary>
[Key(2)]
public bool needsToBeCensored { get; set; }
}

View File

@ -1,30 +0,0 @@
using RabbitMQ.Client;
namespace ChatRoomContract;
public static class RabbitMQConfig
{
public static string ExchangeName = "T120B180.ChatRoom.Exchange";
public static string ServerQueueName = "T120B180.ChatRoom.Server";
public static string ClientQueueNamePrefix = "T120B180.ChatRoom.Client_";
public static string HostName = "localhost";
public static string Username = "guest";
public static string Password = "guest";
public static string CreateClientQueueName()
{
var ClientId = Guid.NewGuid().ToString();
return ClientQueueNamePrefix + ClientId;
}
public static IConnection CreateConnection()
{
var rmqConnectionFactory = new ConnectionFactory
{
HostName = HostName,
UserName = Username,
Password = Password
};
return rmqConnectionFactory.CreateConnection();
}
}

View File

@ -6,7 +6,7 @@ namespace ChatRoomContract;
/// <summary>
/// Wrapper around generated NSwag client
/// </summary>
public class ChatRoomClient : IChatRoomService
public class RestChatRoomClient : IChatRoomService
{
NswagGenerated.NswagChatRoomClient nswag;
@ -15,7 +15,7 @@ public class ChatRoomClient : IChatRoomService
/// </summary>
/// <param name="baseUrl">Server base url</param>
/// <param name="httpClient">HTTP Client</param>
public ChatRoomClient(string baseUrl, HttpClient httpClient)
public RestChatRoomClient(string baseUrl, HttpClient httpClient)
{
nswag = new NswagGenerated.NswagChatRoomClient(baseUrl, httpClient);
}

View File

@ -1,4 +1,4 @@
{
{
"runtime": "Net60",
"documentGenerator": {
"fromDocument": {

View File

@ -1,4 +1,5 @@
using NLog;
using Microsoft.Extensions.Logging;
using NLog;
namespace ChatRoom;

View File

@ -1,13 +1,10 @@
namespace ChatRoom;

namespace ChatRoom;
public class ChatRoomSimpleRPCService : ChatRoomContract.IChatRoomService
public class ChatRoomService : ChatRoomContract.IChatRoomService
{
private readonly ChatRoomLogic logic;
public ChatRoomSimpleRPCService(ChatRoomLogic logic)
{
this.logic = logic;
}
//NOTE: instance-per-request service would need logic to be static or injected from a singleton instance
private readonly ChatRoomLogic logic = new ChatRoomLogic();
public int RegisterClient(string name)
{

View File

@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />
<PackageReference Include="SimpleRpc.Serialization.Hyperion" Version="1.0.0-beta1" />
@ -16,7 +14,6 @@
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Moderator\Moderator.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,100 @@
using System.Net;
using NLog;
using SimpleRpc.Transports;
using SimpleRpc.Transports.Http.Server;
using SimpleRpc.Serialization.Hyperion;
using ChatRoomContract;
using Microsoft.Extensions.Logging;
namespace ChatRoom;
public class Server
{
/// <summary>
/// Logger for this class.
/// </summary>
Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Configure loggin subsystem.
/// </summary>
private void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
/// <summary>
/// Program entry point.
/// </summary>
/// <param name="args">Command line arguments.</param>
public static void Main(string[] args)
{
var self = new Server();
self.Run(args);
}
/// <summary>
/// Program body.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void Run(string[] args)
{
//configure logging
ConfigureLogging();
//indicate server is about to start
log.Info("Server is about to start");
//start the server
StartServer(args);
}
/// <summary>
/// Starts integrated server.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void StartServer(string[] args)
{
Console.Title = "Chat Room";
///create web app builder
var builder = WebApplication.CreateBuilder(args);
//configure integrated server
builder.WebHost.ConfigureKestrel(opts => {
opts.Listen(IPAddress.Loopback, 5000);
});
//add SimpleRPC services
builder.Services
.AddSimpleRpcServer(new HttpServerTransportOptions { Path = "/simplerpc" })
.AddSimpleRpcHyperionSerializer();
//add our custom services
builder.Services
// .AddScoped<IChatRoomService, ChatRoomService>(); //instance-per-request, AddTransient would result in the same
.AddSingleton<IChatRoomService>(new ChatRoomService()); //singleton
//build the server
var app = builder.Build();
//add SimpleRPC middleware
app.UseSimpleRpcServer();
//run the server
app.Run();
// app.RunAsync(); //use this if you need to implement background processing in the main thread
}
}

View File

@ -1,15 +1,18 @@
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using ChatRoomContract.Protocol;
using ChatRoomContract;
namespace ChatRoom;
namespace GrpcToSimpleRPCAdapter;
public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBase
public class ChatRoomService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBase
{
private readonly ChatRoomLogic logic;
public ChatRoomGrpcService(ChatRoomLogic logic)
//NOTE: instance-per-request service would need logic to be static or injected from a singleton instance
private readonly IChatRoomService service;
public ChatRoomService(IChatRoomService service)
{
this.logic = logic;
this.service = service;
}
/// <summary>
@ -20,7 +23,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Client ID</returns>
public override Task<ClientId> RegisterClient(RegisterClientRequest request, ServerCallContext context)
{
var result = new ClientId { Id = logic.RegisterClient(request.Name) };
var result = new ClientId { Id = service.RegisterClient(request.Name) };
return Task.FromResult(result);
}
@ -32,7 +35,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Number of strikes</returns>
public override Task<Srikes> GetStrikes(ClientId request, ServerCallContext context)
{
var result = new Srikes { Strikes = logic.GetStrikes(request.Id) };
var result = new Srikes { Strikes = service.GetStrikes(request.Id) };
return Task.FromResult(result);
}
@ -44,7 +47,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Optional datetime object</returns>
public override Task<BlockedUntil> GetBlockedUntil(ClientId request, ServerCallContext context)
{
var timestamp = logic.GetBlockedUntil(request.Id);
var timestamp = service.GetBlockedUntil(request.Id);
var result = new BlockedUntil { HasTimestamp = false };
if (timestamp != null)
{
@ -62,7 +65,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Was sending successful, can fail if user is blocked</returns>
public override Task<BoolResponse> SendMessage(UserMessageRequest request, ServerCallContext context)
{
var success = logic.SendMessage(request.ClientId, request.Contents, request.NeedsToBeCensored);
var success = service.SendMessage(request.ClientId, request.Contents, request.NeedsToBeCensored);
var result = new BoolResponse { Success = success };
return Task.FromResult(result);
}
@ -75,7 +78,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Message object. Returns null if there is no message</returns>
public override Task<NewUserMessage> GetNewMessage(Empty request, ServerCallContext context)
{
var message = logic.GetNewMessage();
var message = service.GetNewMessage();
var result = new NewUserMessage { HasMessage = false, Message = new UserMessage() };
if (message != null)
{
@ -95,7 +98,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Empty</returns>
public override Task<Empty> ApproveMessage(MessageId request, ServerCallContext context)
{
logic.ApproveMessage(request.Id);
service.ApproveMessage(request.Id);
return Task.FromResult(new Empty());
}
@ -107,7 +110,7 @@ public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBa
/// <returns>Empty</returns>
public override Task<Empty> RejectMessage(MessageId request, ServerCallContext context)
{
logic.RejectMessage(request.Id);
service.RejectMessage(request.Id);
return Task.FromResult(new Empty());
}
}

View File

@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />
<PackageReference Include="SimpleRpc.Serialization.Hyperion" Version="1.0.0-beta1" />
@ -16,7 +14,6 @@
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Participant\Participant.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
{
"profiles": {
"ChatRoom": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:56849;http://localhost:56850"
}
}
}

View File

@ -0,0 +1,122 @@
using ChatRoomContract;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Logging;
using SimpleRpc.Serialization.Hyperion;
using SimpleRpc.Transports;
using SimpleRpc.Transports.Http.Client;
using System.Diagnostics;
using System.Net;
using NLog;
namespace GrpcToSimpleRPCAdapter;
public class Server
{
public static void Main(string[] args)
{
var self = new Server();
self.Run(args);
}
/// <summary>
/// Logger for this class.
/// </summary>
Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Configures logging subsystem.
/// </summary>
private void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
/// <summary>
/// Program body.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void Run(string[] args)
{
//configure logging
ConfigureLogging();
//indicate server is about to start
log.Info("Server is about to start");
//start the server
StartServer(args);
}
public IChatRoomService getSimpleRPCService()
{
//connect to the server, get service client proxy
var sc = new ServiceCollection();
sc
.AddSimpleRpcClient(
"chatRoomService", //must be same as on line 86
new HttpClientTransportOptions
{
Url = "http://127.0.0.1:5000/simplerpc",
Serializer = "HyperionMessageSerializer"
}
)
.AddSimpleRpcHyperionSerializer();
sc.AddSimpleRpcProxy<IChatRoomService>("chatRoomService"); //must be same as on line 77
var sp = sc.BuildServiceProvider();
var chatRoom = sp.GetService<IChatRoomService>();
Debug.Assert(chatRoom != null);
return chatRoom;
}
/// <summary>
/// Starts integrated server.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void StartServer(string[] args)
{
//create web app builder
var builder = WebApplication.CreateBuilder(args);
//configure integrated server
builder.WebHost.ConfigureKestrel(opts => {
opts.Listen(IPAddress.Loopback, 5002, opts =>
{
opts.Protocols = HttpProtocols.Http2;
});
});
//add support for GRPC services
builder.Services.AddGrpc();
//add the actual services
builder.Services.AddSingleton(getSimpleRPCService());
//build the server
var app = builder.Build();
//turn on request routing
app.UseRouting();
//configure routes
app.MapGrpcService<ChatRoomService>();
//run the server
app.Run();
// app.RunAsync(); //use this if you need to implement background processing in the main thread
}
}

View File

@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -3,29 +3,17 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35208.52
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatRoom", "ChatRoom\ChatRoom.csproj", "{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestToSimpleRPCAdapter", "RestToSimpleRPCAdapter\RestToSimpleRPCAdapter.csproj", "{342A1012-23DF-4D2C-9E37-D8B6175C0D55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatRoomContract", "ChatRoomContract\ChatRoomContract.csproj", "{301234B6-13D6-4947-BCEC-1287E7CFCA6F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatRoomContract", "ChatRoomContract\ChatRoomContract.csproj", "{82F1B5A7-7D2E-40B8-9DFE-0B752A00272F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorSimpleRPC", "ModeratorSimpleRPC\ModeratorSimpleRPC.csproj", "{1258DF7D-83C6-49AF-8A47-4A06F816846F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatRoomSimpleRPC", "ChatRoomSimpleRPC\ChatRoomSimpleRPC.csproj", "{4F2C26C6-F0A2-4427-82E9-9619831EEBE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantSimpleRPC", "ParticipantSimpleRPC\ParticipantSimpleRPC.csproj", "{1228253D-45D1-477F-93F3-99C2741CF827}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorRest", "ModeratorRest\ModeratorRest.csproj", "{ED518130-CADD-4D9F-8E42-48DBB43DD131}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorRest", "ModeratorRest\ModeratorRest.csproj", "{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcToSimpleRPCAdapter", "GrpcToSimpleRPCAdapter\GrpcToSimpleRPCAdapter.csproj", "{793C59B0-7712-49A5-8A49-C1527FCA4080}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantRest", "ParticipantRest\ParticipantRest.csproj", "{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moderator", "Moderator\Moderator.csproj", "{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Participant", "Participant\Participant.csproj", "{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorGrpc", "ModeratorGrpc\ModeratorGrpc.csproj", "{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantGrpc", "ParticipantGrpc\ParticipantGrpc.csproj", "{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorRabbitMQ", "ModeratorRabbitMQ\ModeratorRabbitMQ.csproj", "{065DF942-2AFF-44A0-AA7A-44CCC5446848}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantRabbitMQ", "ParticipantRabbitMQ\ParticipantRabbitMQ.csproj", "{9434774F-B80A-490F-A57A-52031CC2F5C6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantGrpc", "ParticipantGrpc\ParticipantGrpc.csproj", "{798A8C74-E96E-455D-81DB-AFFBA53140FB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -33,59 +21,35 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Release|Any CPU.Build.0 = Release|Any CPU
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Release|Any CPU.Build.0 = Release|Any CPU
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Release|Any CPU.Build.0 = Release|Any CPU
{1228253D-45D1-477F-93F3-99C2741CF827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1228253D-45D1-477F-93F3-99C2741CF827}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1228253D-45D1-477F-93F3-99C2741CF827}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1228253D-45D1-477F-93F3-99C2741CF827}.Release|Any CPU.Build.0 = Release|Any CPU
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Release|Any CPU.Build.0 = Release|Any CPU
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Release|Any CPU.Build.0 = Release|Any CPU
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Release|Any CPU.Build.0 = Release|Any CPU
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Release|Any CPU.Build.0 = Release|Any CPU
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Release|Any CPU.Build.0 = Release|Any CPU
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Release|Any CPU.Build.0 = Release|Any CPU
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Debug|Any CPU.Build.0 = Debug|Any CPU
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Release|Any CPU.ActiveCfg = Release|Any CPU
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Release|Any CPU.Build.0 = Release|Any CPU
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Release|Any CPU.Build.0 = Release|Any CPU
{342A1012-23DF-4D2C-9E37-D8B6175C0D55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{342A1012-23DF-4D2C-9E37-D8B6175C0D55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{342A1012-23DF-4D2C-9E37-D8B6175C0D55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{342A1012-23DF-4D2C-9E37-D8B6175C0D55}.Release|Any CPU.Build.0 = Release|Any CPU
{82F1B5A7-7D2E-40B8-9DFE-0B752A00272F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82F1B5A7-7D2E-40B8-9DFE-0B752A00272F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82F1B5A7-7D2E-40B8-9DFE-0B752A00272F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82F1B5A7-7D2E-40B8-9DFE-0B752A00272F}.Release|Any CPU.Build.0 = Release|Any CPU
{4F2C26C6-F0A2-4427-82E9-9619831EEBE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F2C26C6-F0A2-4427-82E9-9619831EEBE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F2C26C6-F0A2-4427-82E9-9619831EEBE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F2C26C6-F0A2-4427-82E9-9619831EEBE0}.Release|Any CPU.Build.0 = Release|Any CPU
{ED518130-CADD-4D9F-8E42-48DBB43DD131}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED518130-CADD-4D9F-8E42-48DBB43DD131}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED518130-CADD-4D9F-8E42-48DBB43DD131}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED518130-CADD-4D9F-8E42-48DBB43DD131}.Release|Any CPU.Build.0 = Release|Any CPU
{793C59B0-7712-49A5-8A49-C1527FCA4080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{793C59B0-7712-49A5-8A49-C1527FCA4080}.Debug|Any CPU.Build.0 = Debug|Any CPU
{793C59B0-7712-49A5-8A49-C1527FCA4080}.Release|Any CPU.ActiveCfg = Release|Any CPU
{793C59B0-7712-49A5-8A49-C1527FCA4080}.Release|Any CPU.Build.0 = Release|Any CPU
{798A8C74-E96E-455D-81DB-AFFBA53140FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{798A8C74-E96E-455D-81DB-AFFBA53140FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{798A8C74-E96E-455D-81DB-AFFBA53140FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{798A8C74-E96E-455D-81DB-AFFBA53140FB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2D31C66F-9644-49FC-B5F8-3FA3EE49FAC8}
SolutionGuid = {A6CFB064-4E95-4F77-853B-34C8245B775B}
EndGlobalSection
EndGlobal

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
</ItemGroup>
</Project>

View File

@ -1,49 +0,0 @@
using ChatRoomContract;
using NLog;
using Bogus;
namespace Moderator;
public class ModeratorLogic
{
private IChatRoomService chatRoom;
public ModeratorLogic(IChatRoomService chatRoom)
{
this.chatRoom = chatRoom;
}
public void Run()
{
Logger log = LogManager.GetCurrentClassLogger();
var faker = new Faker("en");
var name = faker.Name.FullName();
int clientId = chatRoom.RegisterClient(name);
log.Info($"Registered with client id {clientId}");
Console.Title = $"Moderator | {name} | {clientId}";
while (true)
{
var message = chatRoom.GetNewMessage();
if (message != null)
{
log.Info($"Checking message ({message.id}): {message.contents}");
Thread.Sleep(500);
if (message.needsToBeCensored)
{
chatRoom.RejectMessage(message.id);
}
else
{
chatRoom.ApproveMessage(message.id);
}
}
Thread.Sleep(1 * 1000);
}
}
}

View File

@ -1,52 +0,0 @@
using ChatRoomContract;
using NLog;
using Moderator;
namespace ModeratorGrpc;
internal class Moderator
{
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
static void Main(string[] args)
{
Logger log = LogManager.GetCurrentClassLogger();
ConfigureLogging();
while (true)
{
var client = new ChatRoomGrpcClient("http://127.0.0.1:5000");
var moderator = new ModeratorLogic(client);
try
{
moderator.Run();
}
catch (Exception e)
{
//log whatever exception to console
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
//prevent console spamming
Thread.Sleep(2000);
}
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Moderator\Moderator.csproj" />
</ItemGroup>
</Project>

View File

@ -1,61 +0,0 @@
using ChatRoomContract;
using NLog;
using Bogus;
using Moderator;
namespace ModeratorRabbitMQ;
internal class Moderator
{
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
/// <summary>
/// Entry point
/// </summary>
static void Main()
{
Logger log = LogManager.GetCurrentClassLogger();
ConfigureLogging();
while (true)
{
var chatRoom = new ChatRoomRabbitMQClient(
RabbitMQConfig.CreateConnection(),
RabbitMQConfig.ExchangeName,
RabbitMQConfig.CreateClientQueueName(),
RabbitMQConfig.ServerQueueName
);
var moderator = new ModeratorLogic(chatRoom);
try
{
moderator.Run();
}
catch (Exception e)
{
//log whatever exception to console
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
//prevent console spamming
Thread.Sleep(2000);
}
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Moderator\Moderator.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +1,20 @@
using ChatRoomContract;
using NLog;
using Moderator;
using Bogus;
namespace ModeratorRest;
internal class Moderator
{
/// <summary>
/// Logger for this class.
/// </summary>
Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
private void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
@ -24,20 +29,49 @@ internal class Moderator
LogManager.Configuration = config;
}
static void Main(string[] args)
private void RunConnection(IChatRoomService chatRoom)
{
Logger log = LogManager.GetCurrentClassLogger();
var faker = new Faker("en");
var name = faker.Name.FullName();
int clientId = chatRoom.RegisterClient(name);
log.Info($"Registered with client id {clientId}");
Console.Title = $"Moderator | {name} | {clientId}";
while (true)
{
var message = chatRoom.GetNewMessage();
if (message != null)
{
log.Info($"Checking message ({message.id}): {message.contents}");
Thread.Sleep(500);
if (message.needsToBeCensored)
{
chatRoom.RejectMessage(message.id);
}
else
{
chatRoom.ApproveMessage(message.id);
}
}
Thread.Sleep(1 * 1000);
}
}
private void Run()
{
ConfigureLogging();
while (true)
{
var client = new ChatRoomRestClient("http://127.0.0.1:5000", new HttpClient());
var moderator = new ModeratorLogic(client);
var client = new RestChatRoomClient("http://127.0.0.1:5001", new HttpClient());
try
{
moderator.Run();
RunConnection(client);
}
catch (Exception e)
{
@ -49,4 +83,10 @@ internal class Moderator
}
}
}
static void Main(string[] args)
{
var self = new Moderator();
self.Run();
}
}

View File

@ -8,12 +8,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Moderator\Moderator.csproj" />
</ItemGroup>
</Project>

View File

@ -1,81 +0,0 @@
using ChatRoomContract;
using Microsoft.Extensions.DependencyInjection;
using NLog;
using SimpleRpc.Transports.Http.Client;
using SimpleRpc.Serialization.Hyperion;
using SimpleRpc.Transports;
using System.Diagnostics;
using Moderator;
namespace ModeratorSimpleRPC;
internal class Moderator
{
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
static void Main(string[] args)
{
Logger log = LogManager.GetCurrentClassLogger();
//configure logging
ConfigureLogging();
//initialize random number generator
var rnd = new Random();
while (true)
{
//connect to the server, get service client proxy
var sc = new ServiceCollection();
sc
.AddSimpleRpcClient(
"chatRoomService", //must be same as on line 86
new HttpClientTransportOptions
{
Url = "http://127.0.0.1:5001/simplerpc",
Serializer = "HyperionMessageSerializer"
}
)
.AddSimpleRpcHyperionSerializer();
sc.AddSimpleRpcProxy<IChatRoomService>("chatRoomService"); //must be same as on line 77
var sp = sc.BuildServiceProvider();
var chatRoom = sp.GetService<IChatRoomService>();
Debug.Assert(chatRoom != null);
var moderator = new ModeratorLogic(chatRoom);
try
{
moderator.Run();
}
catch (Exception e)
{
//log whatever exception to console
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
//prevent console spamming
Thread.Sleep(2000);
}
}
}
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
</ItemGroup>
</Project>

View File

@ -1,54 +0,0 @@
using ChatRoomContract;
using Bogus;
using NLog;
namespace Participant;
public class ParticipantLogic
{
private IChatRoomService chatRoom;
public ParticipantLogic(IChatRoomService chatRoom)
{
this.chatRoom = chatRoom;
}
public void Run()
{
Logger log = LogManager.GetCurrentClassLogger();
var faker = new Faker("en");
var rnd = new Random();
var name = faker.Name.FullName();
int clientId = chatRoom.RegisterClient(name);
log.Info($"Registered with client id {clientId}");
while (true)
{
int strikes = chatRoom.GetStrikes(clientId);
Console.Title = $"Participant | {name} | {clientId} | {strikes} Strikes";
var message = string.Join(" ", faker.Lorem.Words(5));
bool needsToBeCensored = rnd.Next(0, 100) > 50;
if (chatRoom.SendMessage(clientId, message, needsToBeCensored))
{
log.Info("Sent message");
}
else
{
log.Info("Failed to send message, blocked");
var blockedUntil = chatRoom.GetBlockedUntil(clientId);
var now = DateTime.UtcNow;
if (blockedUntil != null && blockedUntil > now)
{
var delta = blockedUntil.Value - now;
log.Info($"Waiting {delta.TotalSeconds:F3}s until block expires");
Thread.Sleep((int)delta.TotalMilliseconds);
}
}
Thread.Sleep(2 * 1000);
}
}
}

View File

@ -1,16 +1,20 @@
using ChatRoomContract;
using NLog;
using Bogus;
using Participant;
namespace ParticipantGrpc;
namespace Participant;
internal class Participant
{
/// <summary>
/// Logger for this class.
/// </summary>
Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
private void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
@ -25,20 +29,54 @@ internal class Participant
LogManager.Configuration = config;
}
static void Main(string[] args)
private void RunConnection(IChatRoomService chatRoom)
{
Logger log = LogManager.GetCurrentClassLogger();
var faker = new Faker("en");
var rnd = new Random();
var name = faker.Name.FullName();
int clientId = chatRoom.RegisterClient(name);
log.Info($"Registered with client id {clientId}");
while (true)
{
int strikes = chatRoom.GetStrikes(clientId);
Console.Title = $"Participant | {name} | {clientId} | {strikes} Strikes";
var message = string.Join(" ", faker.Lorem.Words(5));
bool needsToBeCensored = rnd.Next(0, 100) > 50;
if (chatRoom.SendMessage(clientId, message, needsToBeCensored))
{
log.Info("Sent message");
}
else
{
log.Info("Failed to send message, blocked");
var blockedUntil = chatRoom.GetBlockedUntil(clientId);
var now = DateTime.UtcNow;
if (blockedUntil != null && blockedUntil > now)
{
var delta = blockedUntil.Value - now;
log.Info($"Waiting {delta.TotalSeconds:F3}s until block expires");
Thread.Sleep((int)delta.TotalMilliseconds);
}
}
Thread.Sleep(2 * 1000);
}
}
private void Run()
{
ConfigureLogging();
while (true)
{
var client = new ChatRoomGrpcClient("http://127.0.0.1:5000");
var participant = new ParticipantLogic(client);
var client = new GrpcChatRoomClient("http://127.0.0.1:5002");
try
{
participant.Run();
RunConnection(client);
}
catch (Exception e)
{
@ -50,4 +88,10 @@ internal class Participant
}
}
}
static void Main(string[] args)
{
var self = new Participant();
self.Run();
}
}

View File

@ -8,12 +8,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Participant\Participant.csproj" />
</ItemGroup>
</Project>

View File

@ -1,60 +0,0 @@
using ChatRoomContract;
using NLog;
using Participant;
namespace ParticipantRabbitMQ;
internal class Participant
{
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
/// <summary>
/// Entry point
/// </summary>
static void Main()
{
Logger log = LogManager.GetCurrentClassLogger();
ConfigureLogging();
while (true)
{
var chatRoom = new ChatRoomRabbitMQClient(
RabbitMQConfig.CreateConnection(),
RabbitMQConfig.ExchangeName,
RabbitMQConfig.CreateClientQueueName(),
RabbitMQConfig.ServerQueueName
);
var participant = new ParticipantLogic(chatRoom);
try
{
participant.Run();
}
catch (Exception e)
{
//log whatever exception to console
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
//prevent console spamming
Thread.Sleep(2000);
}
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Participant\Participant.csproj" />
</ItemGroup>
</Project>

View File

@ -1,52 +0,0 @@
using ChatRoomContract;
using NLog;
using Participant;
namespace ParticipantRest;
internal class Participant
{
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
static void Main(string[] args)
{
Logger log = LogManager.GetCurrentClassLogger();
ConfigureLogging();
while (true)
{
var client = new ChatRoomRestClient("http://127.0.0.1:5000", new HttpClient());
var participant = new ParticipantLogic(client);
try
{
participant.Run();
}
catch (Exception e)
{
//log whatever exception to console
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
//prevent console spamming
Thread.Sleep(2000);
}
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
<ProjectReference Include="..\Participant\Participant.csproj" />
</ItemGroup>
</Project>

View File

@ -1,81 +0,0 @@
using ChatRoomContract;
using Microsoft.Extensions.DependencyInjection;
using NLog;
using SimpleRpc.Transports.Http.Client;
using SimpleRpc.Serialization.Hyperion;
using SimpleRpc.Transports;
using System.Diagnostics;
using Participant;
namespace ParticipantSimpleRPC;
internal class Participant
{
/// <summary>
/// Configures logging subsystem.
/// </summary>
private static void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
static void Main(string[] args)
{
Logger log = LogManager.GetCurrentClassLogger();
//configure logging
ConfigureLogging();
//initialize random number generator
var rnd = new Random();
while (true)
{
//connect to the server, get service client proxy
var sc = new ServiceCollection();
sc
.AddSimpleRpcClient(
"chatRoomService", //must be same as on line 86
new HttpClientTransportOptions
{
Url = "http://127.0.0.1:5001/simplerpc",
Serializer = "HyperionMessageSerializer"
}
)
.AddSimpleRpcHyperionSerializer();
sc.AddSimpleRpcProxy<IChatRoomService>("chatRoomService"); //must be same as on line 77
var sp = sc.BuildServiceProvider();
var chatRoom = sp.GetService<IChatRoomService>();
Debug.Assert(chatRoom != null);
var participant = new ParticipantLogic(chatRoom);
try
{
participant.Run();
}
catch (Exception e)
{
//log whatever exception to console
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
//prevent console spamming
Thread.Sleep(2000);
}
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using ChatRoomContract;
using Microsoft.AspNetCore.Mvc;
namespace ChatRoom;
@ -9,20 +10,20 @@ namespace ChatRoom;
/// </summary>
[Route("chatRoom")]
[ApiController]
public class ChatRoomRestController : ControllerBase
public class ChatRoomController : ControllerBase
{
/// <summary>
/// Service logic. This is created in Server.StartServer() and received through DI in constructor.
/// </summary>
private readonly ChatRoomLogic logic;
private readonly IChatRoomService service;
/// <summary>
/// Constructor
/// </summary>
/// <param name="logic">Logic to use. This will get passed through DI.</param>
public ChatRoomRestController(ChatRoomLogic logic)
public ChatRoomController(IChatRoomService service)
{
this.logic = logic;
this.service = service;
}
/// <summary>
@ -33,7 +34,7 @@ public class ChatRoomRestController : ControllerBase
[HttpPost("/registerClient")]
public ActionResult<int> RegisterClient(string name)
{
return logic.RegisterClient(name);
return service.RegisterClient(name);
}
/// <summary>
@ -44,7 +45,7 @@ public class ChatRoomRestController : ControllerBase
[HttpGet("/getStrikes")]
public ActionResult<int> GetStrikes(int clientId)
{
return logic.GetStrikes(clientId);
return service.GetStrikes(clientId);
}
/// <summary>
@ -55,7 +56,7 @@ public class ChatRoomRestController : ControllerBase
[HttpGet("/getBlockedUntil")]
public ActionResult<DateTime?> GetBlockedUntil(int clientId)
{
return logic.GetBlockedUntil(clientId);
return service.GetBlockedUntil(clientId);
}
/// <summary>
@ -68,7 +69,7 @@ public class ChatRoomRestController : ControllerBase
[HttpPost("/sendMessage")]
public ActionResult<bool> SendMessage(int clientId, string contents, bool needsToBeCensored)
{
return logic.SendMessage(clientId, contents, needsToBeCensored);
return service.SendMessage(clientId, contents, needsToBeCensored);
}
/// <summary>
@ -78,7 +79,7 @@ public class ChatRoomRestController : ControllerBase
[HttpGet("/getNewMessage")]
public ActionResult<ChatRoomContract.Message?> GetNewMessage()
{
return logic.GetNewMessage();
return service.GetNewMessage();
}
/// <summary>
@ -88,7 +89,7 @@ public class ChatRoomRestController : ControllerBase
[HttpPost("/rejectMessage")]
public void RejectMessage(int messageId)
{
logic.RejectMessage(messageId);
service.RejectMessage(messageId);
}
/// <summary>
@ -98,6 +99,6 @@ public class ChatRoomRestController : ControllerBase
[HttpPost("/approveMessage")]
public void ApproveMessage(int messageId)
{
logic.ApproveMessage(messageId);
service.ApproveMessage(messageId);
}
}

View File

@ -0,0 +1,12 @@
{
"profiles": {
"ChatRoom": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:55035;http://localhost:55036"
}
}
}

View File

@ -4,15 +4,13 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />
<PackageReference Include="SimpleRpc.Serialization.Hyperion" Version="1.0.0-beta1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,147 @@
using Microsoft.Extensions.Logging;
using System.Net;
using System.Text.Json.Serialization;
using NLog;
using ChatRoomContract;
using System.Diagnostics;
using SimpleRpc.Transports;
using SimpleRpc.Transports.Http.Client;
using SimpleRpc.Serialization.Hyperion;
namespace RestToSimpleRPCAdapter;
public class Server
{
/// <summary>
/// Logger for this class.
/// </summary>
Logger log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Program entry point.
/// </summary>
/// <param name="args">Command line arguments.</param>
public static void Main(string[] args)
{
var self = new Server();
self.Run(args);
}
/// <summary>
/// Program body.
/// </summary>
/// <param name="args">Command line arguments.</param>
private void Run(string[] args)
{
//configure logging
ConfigureLogging();
//indicate server is about to start
log.Info("Server is about to start");
//start the server
StartServer(args);
}
/// <summary>
/// Configure loggin subsystem.
/// </summary>
private void ConfigureLogging()
{
var config = new NLog.Config.LoggingConfiguration();
var console =
new NLog.Targets.ConsoleTarget("console")
{
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
};
config.AddTarget(console);
config.AddRuleForAllLevels(console);
LogManager.Configuration = config;
}
public IChatRoomService getSimpleRPCService()
{
//connect to the server, get service client proxy
var sc = new ServiceCollection();
sc
.AddSimpleRpcClient(
"chatRoomService", //must be same as on line 86
new HttpClientTransportOptions
{
Url = "http://127.0.0.1:5000/simplerpc",
Serializer = "HyperionMessageSerializer"
}
)
.AddSimpleRpcHyperionSerializer();
sc.AddSimpleRpcProxy<IChatRoomService>("chatRoomService"); //must be same as on line 77
var sp = sc.BuildServiceProvider();
var chatRoom = sp.GetService<IChatRoomService>();
Debug.Assert(chatRoom != null);
return chatRoom;
}
/// <summary>
/// Starts integrated server.
/// </summary>
/// <param name="args">Command line arguments.</param>
public void StartServer(string[] args)
{
//create web app builder
var builder = WebApplication.CreateBuilder(args);
//configure integrated server
builder.WebHost.ConfigureKestrel(opts =>
{
opts.Listen(IPAddress.Loopback, 5001);
});
//turn on support for web api controllers
builder.Services
.AddControllers()
.AddJsonOptions(opts =>
{
//this makes enumeration values to be strings instead of integers in opeanapi doc
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
//add CORS policies
builder.Services.AddCors(cr =>
{
//allow everything from everywhere
cr.AddPolicy("allowAll", cp =>
{
cp.AllowAnyOrigin();
cp.AllowAnyMethod();
cp.AllowAnyHeader();
});
});
//publish the background logic as (an internal) service through dependency injection,
//otherwise it will not start until the first client calls into controller
builder.Services.AddSingleton(getSimpleRPCService());
//build the server
var app = builder.Build();
//turn CORS policy on
app.UseCors("allowAll");
//turn on request routing
app.UseRouting();
//configure routes
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}"
);
//run the server
app.Run();
}
}

View File

@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
}
},
"AllowedHosts": "*"
}