diff --git a/BlazorChat/Client/BlazorChat.Client.csproj b/BlazorChat/Client/BlazorChat.Client.csproj index 56fd9c8..d5094c7 100644 --- a/BlazorChat/Client/BlazorChat.Client.csproj +++ b/BlazorChat/Client/BlazorChat.Client.csproj @@ -1,16 +1,18 @@ - + - net5.0 + net6.0 - - - - + + + + + + diff --git a/BlazorChat/Client/Generated/ChatClientGenerated.cs b/BlazorChat/Client/Generated/ChatClientGenerated.cs new file mode 100644 index 0000000..302f9c8 --- /dev/null +++ b/BlazorChat/Client/Generated/ChatClientGenerated.cs @@ -0,0 +1,888 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#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 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?'" + +namespace ChatClientApi +{ + using BlazorChat.Shared; + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ChatClientGenerated + { + private string _baseUrl = "https://localhost:5001"; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public ChatClientGenerated(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + 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); + /// A server side error occurred. + public System.Threading.Tasks.Task> GetConversationAsync(string contactId) + { + return GetConversationAsync(contactId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetConversationAsync(string contactId, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/{contactId}"); + urlBuilder_.Replace("{contactId}", System.Uri.EscapeDataString(ConvertToString(contactId, System.Globalization.CultureInfo.InvariantCulture))); + + 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("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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>(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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task> GetUsersAsync() + { + return GetUsersAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetUsersAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users"); + + 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("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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>(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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task GetUserDetailsAsync(string userId) + { + return GetUserDetailsAsync(userId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task GetUserDetailsAsync(string userId, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users/{userId}"); + urlBuilder_.Replace("{userId}", System.Uri.EscapeDataString(ConvertToString(userId, System.Globalization.CultureInfo.InvariantCulture))); + + 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("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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(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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task UpdateConversationStatusAsync(string userId, ChatStatus status) + { + return UpdateConversationStatusAsync(userId, status, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task UpdateConversationStatusAsync(string userId, ChatStatus status, System.Threading.CancellationToken cancellationToken) + { + if (status == null) + throw new System.ArgumentNullException("status"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users/{userId}/status/{status}"); + urlBuilder_.Replace("{userId}", System.Uri.EscapeDataString(ConvertToString(userId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{status}", System.Uri.EscapeDataString(ConvertToString(status, System.Globalization.CultureInfo.InvariantCulture))); + + 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/octet-stream"); + request_.Method = new System.Net.Http.HttpMethod("PATCH"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task UpdateMsgStatusAsync(long msgId, int status) + { + return UpdateMsgStatusAsync(msgId, status, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task UpdateMsgStatusAsync(long msgId, int status, System.Threading.CancellationToken cancellationToken) + { + if (msgId == null) + throw new System.ArgumentNullException("msgId"); + + if (status == null) + throw new System.ArgumentNullException("status"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users/messages/{msgId}/status/{status}"); + urlBuilder_.Replace("{msgId}", System.Uri.EscapeDataString(ConvertToString(msgId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{status}", System.Uri.EscapeDataString(ConvertToString(status, System.Globalization.CultureInfo.InvariantCulture))); + + 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("PATCH"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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(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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task SaveMessageAsync(ChatMessage message) + { + return SaveMessageAsync(message, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task SaveMessageAsync(ChatMessage message, System.Threading.CancellationToken cancellationToken) + { + if (message == null) + throw new System.ArgumentNullException("message"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(System.Text.Json.JsonSerializer.Serialize(message, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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(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(); + } + } + + protected struct ObjectResponseResult + { + 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> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.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)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.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.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class OidcConfigurationClientGenerated + { + private string _baseUrl = "https://localhost:5001"; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public OidcConfigurationClientGenerated(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + 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); + /// A server side error occurred. + public System.Threading.Tasks.Task GetClientRequestParametersAsync(string clientId) + { + return GetClientRequestParametersAsync(clientId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task GetClientRequestParametersAsync(string clientId, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/_configuration/{clientId}"); + urlBuilder_.Replace("{clientId}", System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))); + + 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("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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 + { + 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> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.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)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.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.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class FileResponse : System.IDisposable + { + private System.IDisposable _client; + private System.IDisposable _response; + + public int StatusCode { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public System.IO.Stream Stream { get; private set; } + + public bool IsPartial + { + get { return StatusCode == 206; } + } + + public FileResponse(int statusCode, System.Collections.Generic.IReadOnlyDictionary> headers, System.IO.Stream stream, System.IDisposable client, System.IDisposable response) + { + StatusCode = statusCode; + Headers = headers; + Stream = stream; + _client = client; + _response = response; + } + + public void Dispose() + { + Stream.Dispose(); + if (_response != null) + _response.Dispose(); + if (_client != null) + _client.Dispose(); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.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> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> 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", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 \ No newline at end of file diff --git a/BlazorChat/Client/Generated/config.nswag b/BlazorChat/Client/Generated/config.nswag new file mode 100644 index 0000000..3d07f18 --- /dev/null +++ b/BlazorChat/Client/Generated/config.nswag @@ -0,0 +1,101 @@ +{ + "runtime": "Net50", + "defaultVariables": null, + "documentGenerator": { + "fromDocument": { + "json": "{\r\n \"x-generator\": \"NSwag v13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))\",\r\n \"swagger\": \"2.0\",\r\n \"info\": {\r\n \"title\": \"My Title\",\r\n \"version\": \"1.0.0\"\r\n },\r\n \"host\": \"localhost:5001\",\r\n \"schemes\": [\r\n \"https\"\r\n ],\r\n \"paths\": {\r\n \"/api/Chat/{contactId}\": {\r\n \"get\": {\r\n \"tags\": [\r\n \"Chat\"\r\n ],\r\n \"operationId\": \"Chat_GetConversation\",\r\n \"parameters\": [\r\n {\r\n \"type\": \"string\",\r\n \"name\": \"contactId\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"x-nullable\": true\r\n }\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": true,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"file\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/api/Chat/users\": {\r\n \"get\": {\r\n \"tags\": [\r\n \"Chat\"\r\n ],\r\n \"operationId\": \"Chat_GetUsers\",\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": true,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"file\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/api/Chat/users/{userId}\": {\r\n \"get\": {\r\n \"tags\": [\r\n \"Chat\"\r\n ],\r\n \"operationId\": \"Chat_GetUserDetails\",\r\n \"parameters\": [\r\n {\r\n \"type\": \"string\",\r\n \"name\": \"userId\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"x-nullable\": true\r\n }\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": true,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"file\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/api/Chat/users/{userId}/status/{status}\": {\r\n \"patch\": {\r\n \"tags\": [\r\n \"Chat\"\r\n ],\r\n \"operationId\": \"Chat_UpdateConversationStatus\",\r\n \"parameters\": [\r\n {\r\n \"type\": \"string\",\r\n \"name\": \"userId\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"x-nullable\": true\r\n },\r\n {\r\n \"type\": \"integer\",\r\n \"name\": \"status\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"x-schema\": {\r\n \"$ref\": \"#/definitions/ChatStatus\"\r\n },\r\n \"x-nullable\": false,\r\n \"enum\": [\r\n 0,\r\n 1,\r\n 2\r\n ]\r\n }\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": true,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"file\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/api/Chat/users/messages/{msgId}/status/{status}\": {\r\n \"patch\": {\r\n \"tags\": [\r\n \"Chat\"\r\n ],\r\n \"operationId\": \"Chat_UpdateMsgStatus\",\r\n \"produces\": [\r\n \"text/plain\",\r\n \"application/json\",\r\n \"text/json\"\r\n ],\r\n \"parameters\": [\r\n {\r\n \"type\": \"integer\",\r\n \"name\": \"msgId\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"format\": \"int64\",\r\n \"x-nullable\": false\r\n },\r\n {\r\n \"type\": \"integer\",\r\n \"name\": \"status\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"format\": \"int32\",\r\n \"x-nullable\": false\r\n }\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": false,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"$ref\": \"#/definitions/ChatMessage\"\r\n }\r\n },\r\n \"400\": {\r\n \"x-nullable\": false,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"string\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/api/Chat\": {\r\n \"post\": {\r\n \"tags\": [\r\n \"Chat\"\r\n ],\r\n \"operationId\": \"Chat_SaveMessage\",\r\n \"consumes\": [\r\n \"application/json\",\r\n \"text/json\",\r\n \"application/*+json\"\r\n ],\r\n \"parameters\": [\r\n {\r\n \"name\": \"message\",\r\n \"in\": \"body\",\r\n \"required\": true,\r\n \"schema\": {\r\n \"$ref\": \"#/definitions/ChatMessage\"\r\n },\r\n \"x-nullable\": false\r\n }\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": true,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"file\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/_configuration/{clientId}\": {\r\n \"get\": {\r\n \"tags\": [\r\n \"OidcConfiguration\"\r\n ],\r\n \"operationId\": \"OidcConfiguration_GetClientRequestParameters\",\r\n \"parameters\": [\r\n {\r\n \"type\": \"string\",\r\n \"name\": \"clientId\",\r\n \"in\": \"path\",\r\n \"required\": true,\r\n \"x-nullable\": true\r\n }\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": true,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"file\"\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"/WeatherForecast\": {\r\n \"get\": {\r\n \"tags\": [\r\n \"WeatherForecast\"\r\n ],\r\n \"operationId\": \"WeatherForecast_Get\",\r\n \"produces\": [\r\n \"text/plain\",\r\n \"application/json\",\r\n \"text/json\"\r\n ],\r\n \"responses\": {\r\n \"200\": {\r\n \"x-nullable\": false,\r\n \"description\": \"\",\r\n \"schema\": {\r\n \"type\": \"array\",\r\n \"items\": {\r\n \"$ref\": \"#/definitions/WeatherForecast\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n },\r\n \"definitions\": {\r\n \"ChatStatus\": {\r\n \"type\": \"integer\",\r\n \"description\": \"\",\r\n \"x-enumNames\": [\r\n \"Undelivered\",\r\n \"Delivered\",\r\n \"Seen\"\r\n ],\r\n \"enum\": [\r\n 0,\r\n 1,\r\n 2\r\n ]\r\n },\r\n \"ChatMessage\": {\r\n \"type\": \"object\",\r\n \"required\": [\r\n \"id\",\r\n \"status\",\r\n \"createdDate\"\r\n ],\r\n \"properties\": {\r\n \"id\": {\r\n \"type\": \"integer\",\r\n \"format\": \"int64\"\r\n },\r\n \"fromUserId\": {\r\n \"type\": \"string\"\r\n },\r\n \"toUserId\": {\r\n \"type\": \"string\"\r\n },\r\n \"message\": {\r\n \"type\": \"string\"\r\n },\r\n \"status\": {\r\n \"$ref\": \"#/definitions/ChatStatus\"\r\n },\r\n \"createdDate\": {\r\n \"type\": \"string\",\r\n \"format\": \"date-time\"\r\n },\r\n \"fromUser\": {\r\n \"$ref\": \"#/definitions/ApplicationUser\"\r\n },\r\n \"toUser\": {\r\n \"$ref\": \"#/definitions/ApplicationUser\"\r\n }\r\n }\r\n },\r\n \"ApplicationUser\": {\r\n \"allOf\": [\r\n {\r\n \"$ref\": \"#/definitions/IdentityUser\"\r\n },\r\n {\r\n \"type\": \"object\",\r\n \"required\": [\r\n \"lastSeen\",\r\n \"unreadCount\"\r\n ],\r\n \"properties\": {\r\n \"lastSeen\": {\r\n \"type\": \"string\",\r\n \"format\": \"date-time\"\r\n },\r\n \"unreadCount\": {\r\n \"type\": \"integer\",\r\n \"format\": \"int32\"\r\n },\r\n \"chatMessagesFromUsers\": {\r\n \"type\": \"array\",\r\n \"items\": {\r\n \"$ref\": \"#/definitions/ChatMessage\"\r\n }\r\n },\r\n \"chatMessagesToUsers\": {\r\n \"type\": \"array\",\r\n \"items\": {\r\n \"$ref\": \"#/definitions/ChatMessage\"\r\n }\r\n }\r\n }\r\n }\r\n ]\r\n },\r\n \"IdentityUser\": {\r\n \"allOf\": [\r\n {\r\n \"$ref\": \"#/definitions/IdentityUserOfString\"\r\n },\r\n {\r\n \"type\": \"object\"\r\n }\r\n ]\r\n },\r\n \"IdentityUserOfString\": {\r\n \"type\": \"object\",\r\n \"required\": [\r\n \"emailConfirmed\",\r\n \"phoneNumberConfirmed\",\r\n \"twoFactorEnabled\",\r\n \"lockoutEnabled\",\r\n \"accessFailedCount\"\r\n ],\r\n \"properties\": {\r\n \"id\": {\r\n \"type\": \"string\"\r\n },\r\n \"userName\": {\r\n \"type\": \"string\"\r\n },\r\n \"normalizedUserName\": {\r\n \"type\": \"string\"\r\n },\r\n \"email\": {\r\n \"type\": \"string\"\r\n },\r\n \"normalizedEmail\": {\r\n \"type\": \"string\"\r\n },\r\n \"emailConfirmed\": {\r\n \"type\": \"boolean\"\r\n },\r\n \"passwordHash\": {\r\n \"type\": \"string\"\r\n },\r\n \"securityStamp\": {\r\n \"type\": \"string\"\r\n },\r\n \"concurrencyStamp\": {\r\n \"type\": \"string\"\r\n },\r\n \"phoneNumber\": {\r\n \"type\": \"string\"\r\n },\r\n \"phoneNumberConfirmed\": {\r\n \"type\": \"boolean\"\r\n },\r\n \"twoFactorEnabled\": {\r\n \"type\": \"boolean\"\r\n },\r\n \"lockoutEnd\": {\r\n \"type\": \"string\",\r\n \"format\": \"date-time\"\r\n },\r\n \"lockoutEnabled\": {\r\n \"type\": \"boolean\"\r\n },\r\n \"accessFailedCount\": {\r\n \"type\": \"integer\",\r\n \"format\": \"int32\"\r\n }\r\n }\r\n },\r\n \"WeatherForecast\": {\r\n \"type\": \"object\",\r\n \"required\": [\r\n \"date\",\r\n \"temperatureC\",\r\n \"temperatureF\"\r\n ],\r\n \"properties\": {\r\n \"date\": {\r\n \"type\": \"string\",\r\n \"format\": \"date-time\"\r\n },\r\n \"temperatureC\": {\r\n \"type\": \"integer\",\r\n \"format\": \"int32\"\r\n },\r\n \"summary\": {\r\n \"type\": \"string\"\r\n },\r\n \"temperatureF\": {\r\n \"type\": \"integer\",\r\n \"format\": \"int32\"\r\n }\r\n }\r\n }\r\n }\r\n}", + "url": "https://localhost:5001/swagger/v1/swagger.json", + "output": null, + "newLineBehavior": "Auto" + } + }, + "codeGenerators": { + "openApiToCSharpClient": { + "clientBaseClass": null, + "configurationClass": null, + "generateClientClasses": true, + "generateClientInterfaces": false, + "clientBaseInterface": null, + "injectHttpClient": true, + "disposeHttpClient": true, + "protectedMethods": [], + "generateExceptionClasses": true, + "exceptionClass": "ApiException", + "wrapDtoExceptions": true, + "useHttpClientCreationMethod": false, + "httpClientType": "System.Net.Http.HttpClient", + "useHttpRequestMessageCreationMethod": false, + "useBaseUrl": true, + "generateBaseUrlProperty": true, + "generateSyncMethods": false, + "generatePrepareRequestAndProcessResponseAsAsyncMethods": false, + "exposeJsonSerializerSettings": false, + "clientClassAccessModifier": "public", + "typeAccessModifier": "public", + "generateContractsOutput": false, + "contractsNamespace": null, + "contractsOutputFilePath": null, + "parameterDateTimeFormat": "s", + "parameterDateFormat": "yyyy-MM-dd", + "generateUpdateJsonSerializerSettingsMethod": true, + "useRequestAndResponseSerializationSettings": false, + "serializeTypeInformation": false, + "queryNullValue": "", + "className": "{controller}Client", + "operationGenerationMode": "MultipleClientsFromOperationId", + "additionalNamespaceUsages": [], + "additionalContractNamespaceUsages": [], + "generateOptionalParameters": false, + "generateJsonMethods": false, + "enforceFlagEnums": false, + "parameterArrayType": "System.Collections.Generic.IEnumerable", + "parameterDictionaryType": "System.Collections.Generic.IDictionary", + "responseArrayType": "System.Collections.Generic.ICollection", + "responseDictionaryType": "System.Collections.Generic.IDictionary", + "wrapResponses": false, + "wrapResponseMethods": [], + "generateResponseClasses": true, + "responseClass": "SwaggerResponse", + "namespace": "ChatClientApi", + "requiredPropertiesMustBeDefined": true, + "dateType": "System.DateTimeOffset", + "jsonConverters": null, + "anyType": "object", + "dateTimeType": "System.DateTimeOffset", + "timeType": "System.TimeSpan", + "timeSpanType": "System.TimeSpan", + "arrayType": "System.Collections.Generic.ICollection", + "arrayInstanceType": "System.Collections.ObjectModel.Collection", + "dictionaryType": "System.Collections.Generic.IDictionary", + "dictionaryInstanceType": "System.Collections.Generic.Dictionary", + "arrayBaseType": "System.Collections.ObjectModel.Collection", + "dictionaryBaseType": "System.Collections.Generic.Dictionary", + "classStyle": "Poco", + "jsonLibrary": "SystemTextJson", + "generateDefaultValues": true, + "generateDataAnnotations": true, + "excludedTypeNames": [], + "excludedParameterNames": [], + "handleReferences": false, + "generateImmutableArrayProperties": false, + "generateImmutableDictionaryProperties": false, + "jsonSerializerSettingsTransformationMethod": null, + "inlineNamedArrays": false, + "inlineNamedDictionaries": false, + "inlineNamedTuples": true, + "inlineNamedAny": false, + "generateDtoTypes": false, + "generateOptionalPropertiesAsNullable": false, + "generateNullableReferenceTypes": false, + "templateDirectory": null, + "typeNameGeneratorType": null, + "propertyNameGeneratorType": null, + "enumNameGeneratorType": null, + "checksumCacheEnabled": false, + "serviceHost": null, + "serviceSchemes": null, + "output": "ChatClient.cs", + "newLineBehavior": "Auto" + } + } +} \ No newline at end of file diff --git a/BlazorChat/Client/Managers/ChatClient.cs b/BlazorChat/Client/Managers/ChatClient.cs new file mode 100644 index 0000000..6981268 --- /dev/null +++ b/BlazorChat/Client/Managers/ChatClient.cs @@ -0,0 +1,18 @@ +using ChatClientApi; +using System.Net.Http; +using System.Text.Json; + +namespace BlazorChat.Client.Managers +{ + public class ChatClient : ChatClientGenerated + { + public ChatClient(HttpClient httpClient) : base(httpClient) + { + // https://github.com/dotnet/aspnetcore/issues/21736 + base.ReadResponseAsString = true; + base.JsonSerializerSettings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + } + } + + +} \ No newline at end of file diff --git a/BlazorChat/Client/Pages/Chat.razor b/BlazorChat/Client/Pages/Chat.razor index de73572..fd41e0a 100644 --- a/BlazorChat/Client/Pages/Chat.razor +++ b/BlazorChat/Client/Pages/Chat.razor @@ -1,5 +1,6 @@ @page "/chat/{ContactId}" @page "/chat" +
@@ -17,16 +18,33 @@
@foreach (var message in messages) { -
-
- @message.FromUser.Email.ToUpper().FirstOrDefault() + @if (CurrentUserId == message.ToUserId) + { +
+
+ @message.FromUser.Email.ToUpper().FirstOrDefault() +
+
+ @message.FromUser.Email + @message.CreatedDate.ToString("dd MMM, yyyy hh:mm tt") + @message.Message +
-
- @message.FromUser.Email - @message.CreatedDate.ToString("dd MMM, yyyy hh:mm tt") - @message.Message + } + else + { +
+
+ @message.FromUser.Email.ToUpper().FirstOrDefault() +
+
+ @*@message.FromUser.Email*@ +
@message.CreatedDate.ToString("dd MMM, yyyy hh:mm tt")@message.Status.ToString()
+ @message.Message +
-
+ } + }
@@ -35,37 +53,45 @@ Send - - - # - contacts - -
- - @foreach (var user in ChatUsers) - { - -
-
- - @if (user.Id == ContactId) - { - @user.Email.ToUpper().FirstOrDefault() - } - else - { - @user.Email.ToUpper().FirstOrDefault() - } - -
-
+ + + # + contacts + +
+ + @foreach (var user in ChatUsers) + { + +
+
+ + @if (user.Id == ContactId) + { + @user.Email.ToUpper().FirstOrDefault() + } + else + { + @user.Email.ToUpper().FirstOrDefault() + } + +
+
+ @user.Email - @user.Id -
+ + @user.Id
-
- } -
-
-
-
\ No newline at end of file +
+
+ } +
+
+
+
+ \ No newline at end of file diff --git a/BlazorChat/Client/Pages/Chat.razor.cs b/BlazorChat/Client/Pages/Chat.razor.cs index fae39f3..1d7f1ed 100644 --- a/BlazorChat/Client/Pages/Chat.razor.cs +++ b/BlazorChat/Client/Pages/Chat.razor.cs @@ -4,19 +4,30 @@ using Microsoft.JSInterop; using MudBlazor; using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Microsoft.AspNetCore.Http.Connections; +using Microsoft.Extensions.Logging; +using SignalR.Strong; +using System.Collections.Immutable; +using BlazorChat.Shared.Extensions; +using BlazorChat.Client.Managers; namespace BlazorChat.Client.Pages { - public partial class Chat + public partial class Chat:IAsyncDisposable,IChatClientHub { - [CascadingParameter] public HubConnection hubConnection { get; set; } + [Inject] public ILogger Logger { get; set; } + [Inject] private IAccessTokenProvider TokenProvider { get; set; } + [Inject] private ILoggerProvider LoggerProvider { get; set; } + [Inject] private ChatClient ChatClient { get; set; } + public HubConnection HubConnection { get; set; } [Parameter] public string CurrentMessage { get; set; } [Parameter] public string CurrentUserId { get; set; } [Parameter] public string CurrentUserEmail { get; set; } - private List messages = new List(); + private ImmutableList messages = ImmutableList.Empty; + private IChatServerHub _chatServerHub; private async Task SubmitAsync() { if (!string.IsNullOrEmpty(CurrentMessage) && !string.IsNullOrEmpty(ContactId)) @@ -29,10 +40,10 @@ private async Task SubmitAsync() CreatedDate = DateTime.Now }; - await _chatManager.SaveMessageAsync(chatHistory); - chatHistory.FromUserId = CurrentUserId; - await hubConnection.SendAsync("SendMessageAsync", chatHistory, CurrentUserEmail); + chatHistory = await ChatClient.SaveMessageAsync(chatHistory); + await _chatServerHub.SendMessageAsync(chatHistory, ContactId); CurrentMessage = string.Empty; + messages = messages.Add(chatHistory); } } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -41,57 +52,168 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } protected override async Task OnInitializedAsync() { - if (hubConnection == null) + var state = await _stateProvider.GetAuthenticationStateAsync(); + var user = state.User; + CurrentUserId = user.Claims.Where(a => a.Type == "sub").Select(a => a.Value).FirstOrDefault(); + CurrentUserEmail = user.Claims.Where(a => a.Type == "name").Select(a => a.Value).FirstOrDefault(); + + if (HubConnection == null) { - hubConnection = new HubConnectionBuilder().WithUrl(_navigationManager.ToAbsoluteUri("/signalRHub")).Build(); + Logger.LogWarning("hub created"); + HubConnection = new HubConnectionBuilder() + .WithUrl(_navigationManager.ToAbsoluteUri("/signalRHub"), + opt => + { + opt.Transports = HttpTransportType.WebSockets; + opt.AccessTokenProvider = async () => + { + var token = await TokenProvider.RequestAccessToken(); + token.TryGetToken(out var accessToken); + Logger.LogWarning($"Token: {accessToken.Value}"); + return accessToken.Value; + }; + }) + .ConfigureLogging( logging => + { + logging.AddProvider(LoggerProvider); + logging.SetMinimumLevel(LogLevel.Debug); + }) + .Build(); + await HubConnection.StartAsync(); + _chatServerHub = HubConnection.AsGeneratedHub(); + HubConnection.RegisterSpoke(this); } - if (hubConnection.State == HubConnectionState.Disconnected) + if (HubConnection.State == HubConnectionState.Disconnected) { - await hubConnection.StartAsync(); + await HubConnection.StartAsync(); } - hubConnection.On("ReceiveMessage", async (message, userName) => - { - if ((ContactId == message.ToUserId && CurrentUserId == message.FromUserId) || (ContactId == message.FromUserId && CurrentUserId == message.ToUserId)) - { - - if ((ContactId == message.ToUserId && CurrentUserId == message.FromUserId)) - { - messages.Add(new ChatMessage { Message = message.Message, CreatedDate = message.CreatedDate, FromUser = new ApplicationUser() { Email = CurrentUserEmail } }); - await hubConnection.SendAsync("ChatNotificationAsync", $"New Message From {userName}", ContactId, CurrentUserId); - } - else if ((ContactId == message.FromUserId && CurrentUserId == message.ToUserId)) - { - messages.Add(new ChatMessage { Message = message.Message, CreatedDate = message.CreatedDate, FromUser = new ApplicationUser() { Email = ContactEmail } }); - } - await _jsRuntime.InvokeAsync("ScrollToBottom", "chatContainer"); - StateHasChanged(); - } - }); await GetUsersAsync(); - var state = await _stateProvider.GetAuthenticationStateAsync(); - var user = state.User; - CurrentUserId = user.Claims.Where(a => a.Type == "sub").Select(a => a.Value).FirstOrDefault(); - CurrentUserEmail = user.Claims.Where(a => a.Type == "name").Select(a => a.Value).FirstOrDefault(); if (!string.IsNullOrEmpty(ContactId)) { await LoadUserChat(ContactId); } } - public List ChatUsers = new List(); + public ImmutableList ChatUsers = ImmutableList.Empty; [Parameter] public string ContactEmail { get; set; } [Parameter] public string ContactId { get; set; } - async Task LoadUserChat(string userId) + + private async Task LoadUserChat(string userId) { - var contact = await _chatManager.GetUserDetailsAsync(userId); + var contact = await ChatClient.GetUserDetailsAsync(userId); ContactId = contact.Id; ContactEmail = contact.Email; _navigationManager.NavigateTo($"chat/{ContactId}"); - messages = new List(); - messages = await _chatManager.GetConversationAsync(ContactId); + messages = await ChatClient + .GetConversationAsync(ContactId) + .ToImmutable(); + + ChatUsers = ChatUsers.Update( + x => x.Id == userId, + x => x.UnreadCount = 0); + + await UpdateConversationStatus(userId, ChatStatus.Seen).ConfigureAwait(false); + } + private async Task UpdateConversationStatus(string userId,ChatStatus chatStatus) + { + await ChatClient + .UpdateConversationStatusAsync(userId, ChatStatus.Seen) + .ConfigureAwait(false); + await _chatServerHub.UpdateConversationStatusAsync(userId, CurrentUserId, ChatStatus.Seen) + .ConfigureAwait(false); } private async Task GetUsersAsync() { - ChatUsers = await _chatManager.GetUsersAsync(); + ChatUsers = await ChatClient + .GetUsersAsync() + .ToImmutable(); + } + + public async ValueTask DisposeAsync() + { + Logger.LogWarning("dispose"); + _snackBar?.Dispose(); + await HubConnection.StopAsync(); + await HubConnection.DisposeAsync(); + } + + public async Task ReceiveMessage(ChatMessage message) + { + if ((ContactId == message.ToUserId && CurrentUserId == message.FromUserId) || (ContactId == message.FromUserId && CurrentUserId == message.ToUserId)) + { + + if ((ContactId == message.ToUserId && CurrentUserId == message.FromUserId)) + { + messages= messages.Add(message); + await _chatServerHub.ChatNotificationAsync($"New Message From {ChatUsers.FirstOrDefault(x=> x.Id == message.ToUserId)?.Email}", ContactId, CurrentUserId); + _ = UpdateMessageStatus(message.Id, ChatStatus.Delivered).ConfigureAwait(false); + } + else if ((ContactId == message.FromUserId && CurrentUserId == message.ToUserId)) + { + messages= messages.Add(message); + _ = UpdateMessageStatus(message.Id, ChatStatus.Seen).ConfigureAwait(false); + } + await _jsRuntime.InvokeAsync("ScrollToBottom", "chatContainer"); + + } + else + { + ChatUsers = ChatUsers.Update( + x => x.Id == message.FromUserId, + x => ++x.UnreadCount); + _ = UpdateMessageStatus(message.Id, ChatStatus.Delivered,message.FromUserId).ConfigureAwait(false); + } + StateHasChanged(); + } + private Task UpdateMessageStatus(long id, ChatStatus status, string fromUserId = null) + { + _ = ChatClient + .UpdateMsgStatusAsync(id,(int)status) + .ConfigureAwait(false); + _ = _chatServerHub + .UpdateMessageStatusAsync(id,fromUserId??ContactId, status) + .ConfigureAwait(false); + return Task.CompletedTask; + } + public async Task ReceiveChatNotification(string message, string senderUserId) + { + await _jsRuntime.InvokeAsync("PlayAudio", "notification"); + _snackBar.Add(message, Severity.Info, config => + { + config.VisibleStateDuration = 10000; + config.HideTransitionDuration = 500; + config.ShowTransitionDuration = 500; + config.Action = "Chat?"; + config.ActionColor = Color.Info; + config.Onclick = _ => + { + _navigationManager.NavigateTo($"chat/{senderUserId}"); + return Task.CompletedTask; + }; + }); + } + + public Task ReceiveConversationStatus(string userId, ChatStatus status) + { + if (ContactId == userId) + { + messages.ForEach(x=> x.Status = status); + StateHasChanged(); + } + + return Task.CompletedTask; + } + + public Task ReceiveMessageStatus(long msgId, ChatStatus status) + { + if (!ContactId.IsNullEmpty()) + { + messages = messages.Update( + x => x.Id == msgId, + x => x.Status = status); + StateHasChanged(); + } + + return Task.CompletedTask; } } } diff --git a/BlazorChat/Client/Program.cs b/BlazorChat/Client/Program.cs index 51f9bac..73e1b16 100644 --- a/BlazorChat/Client/Program.cs +++ b/BlazorChat/Client/Program.cs @@ -1,15 +1,11 @@ using BlazorChat.Client.Managers; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using MudBlazor; using MudBlazor.Services; using System; -using System.Collections.Generic; using System.Net.Http; -using System.Text; using System.Threading.Tasks; namespace BlazorChat.Client @@ -21,14 +17,13 @@ public static async Task Main(string[] args) var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); - builder.Services.AddHttpClient("BlazorChat.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) + builder.Services.AddHttpClient("BlazorChat.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) .AddHttpMessageHandler(); // Supply HttpClient instances that include access tokens when making requests to the server project builder.Services.AddScoped(sp => sp.GetRequiredService().CreateClient("BlazorChat.ServerAPI")); builder.Services.AddMudServices(c => { c.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight; }); builder.Services.AddApiAuthorization(); - builder.Services.AddTransient(); await builder.Build().RunAsync(); } } diff --git a/BlazorChat/Client/Properties/launchSettings.json b/BlazorChat/Client/Properties/launchSettings.json index 79115ed..ba0e2ef 100644 --- a/BlazorChat/Client/Properties/launchSettings.json +++ b/BlazorChat/Client/Properties/launchSettings.json @@ -3,25 +3,26 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:48917", - "sslPort": 44373 + "applicationUrl": "http://localhost:57610", + "sslPort": 44386 } }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", + "BlazorChat.Client": { + "commandName": "Project", + "dotnetRunMessages": true, "launchBrowser": true, + "hotReloadProfile": "blazorwasm", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "BlazorChat": { - "commandName": "Project", - "dotnetRunMessages": "true", + "IIS Express": { + "commandName": "IISExpress", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/BlazorChat/Client/Shared/MainLayout.razor b/BlazorChat/Client/Shared/MainLayout.razor index d092916..b6b1b58 100644 --- a/BlazorChat/Client/Shared/MainLayout.razor +++ b/BlazorChat/Client/Shared/MainLayout.razor @@ -18,9 +18,9 @@ - + @**@ @Body - + @**@ @@ -31,34 +31,4 @@ { _drawerOpen = !_drawerOpen; } - private HubConnection hubConnection; - public bool IsConnected => hubConnection.State == HubConnectionState.Connected; - protected override async Task OnInitializedAsync() - { - hubConnection = new HubConnectionBuilder().WithUrl(_navigationManager.ToAbsoluteUri("/signalRHub")).Build(); - await hubConnection.StartAsync(); - hubConnection.On("ReceiveChatNotification", (message, receiverUserId, senderUserId) => - { - if (CurrentUserId == receiverUserId) - { - _jsRuntime.InvokeAsync("PlayAudio", "notification"); - _snackBar.Add(message, Severity.Info, config => - { - config.VisibleStateDuration = 10000; - config.HideTransitionDuration = 500; - config.ShowTransitionDuration = 500; - config.Action = "Chat?"; - config.ActionColor = Color.Info; - config.Onclick = snackbar => - { - _navigationManager.NavigateTo($"chat/{senderUserId}"); - return Task.CompletedTask; - }; - }); - } - }); - var state = await _stateProvider.GetAuthenticationStateAsync(); - var user = state.User; - CurrentUserId = user.Claims.Where(a => a.Type == "sub").Select(a => a.Value).FirstOrDefault(); - } } \ No newline at end of file diff --git a/BlazorChat/Client/_Imports.razor b/BlazorChat/Client/_Imports.razor index 1cc6aaa..f26101a 100644 --- a/BlazorChat/Client/_Imports.razor +++ b/BlazorChat/Client/_Imports.razor @@ -14,7 +14,4 @@ @inject NavigationManager _navigationManager @inject ISnackbar _snackBar @inject IJSRuntime _jsRuntime -@inject AuthenticationStateProvider _stateProvider; - -@using BlazorChat.Client.Managers -@inject IChatManager _chatManager \ No newline at end of file +@inject AuthenticationStateProvider _stateProvider; \ No newline at end of file diff --git a/BlazorChat/Server/BlazorChat.Server.csproj b/BlazorChat/Server/BlazorChat.Server.csproj index 0fd8cf6..c6fb21f 100644 --- a/BlazorChat/Server/BlazorChat.Server.csproj +++ b/BlazorChat/Server/BlazorChat.Server.csproj @@ -1,12 +1,14 @@ - + - net5.0 + net6.0 BlazorChat.Server-BDB24F36-733E-42B8-8ABB-6088ED6E9EDF - + + + @@ -16,12 +18,12 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BlazorChat/Server/Controllers/ChatController.cs b/BlazorChat/Server/Controllers/ChatController.cs index 0e5e703..ceeeec5 100644 --- a/BlazorChat/Server/Controllers/ChatController.cs +++ b/BlazorChat/Server/Controllers/ChatController.cs @@ -1,7 +1,6 @@ using BlazorChat.Server.Data; using BlazorChat.Shared; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -9,64 +8,107 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using BlazorChat.Shared.Extensions; +using System.Collections.Generic; namespace BlazorChat.Server.Controllers { -[Route("api/[controller]")] -[ApiController] -[Authorize] -public class ChatController : ControllerBase -{ - private readonly UserManager _userManager; - private readonly ApplicationDbContext _context; - public ChatController(UserManager userManager, ApplicationDbContext context) + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class ChatController : ControllerBase { - _userManager = userManager; - _context = context; - } -[HttpGet("{contactId}")] -public async Task GetConversationAsync(string contactId) -{ - var userId = User.Claims.Where(a => a.Type == ClaimTypes.NameIdentifier).Select(a => a.Value).FirstOrDefault(); - var messages = await _context.ChatMessages - .Where(h => (h.FromUserId == contactId && h.ToUserId == userId) || (h.FromUserId == userId && h.ToUserId == contactId)) - .OrderBy(a => a.CreatedDate) - .Include(a => a.FromUser) - .Include(a => a.ToUser) - .Select(x => new ChatMessage - { - FromUserId = x.FromUserId, - Message = x.Message, - CreatedDate = x.CreatedDate, - Id = x.Id, - ToUserId = x.ToUserId, - ToUser = x.ToUser, - FromUser = x.FromUser - }).ToListAsync(); - return Ok(messages); -} -[HttpGet("users")] -public async Task GetUsersAsync() -{ - var userId = User.Claims.Where(a => a.Type == ClaimTypes.NameIdentifier).Select(a => a.Value).FirstOrDefault(); - var allUsers = await _context.Users.Where(user => user.Id != userId).ToListAsync(); - return Ok(allUsers); -} -[HttpGet("users/{userId}")] -public async Task GetUserDetailsAsync(string userId) -{ - var user = await _context.Users.Where(user => user.Id == userId).FirstOrDefaultAsync(); - return Ok(user); -} -[HttpPost] -public async Task SaveMessageAsync(ChatMessage message) -{ - var userId = User.Claims.Where(a => a.Type == ClaimTypes.NameIdentifier).Select(a => a.Value).FirstOrDefault(); - message.FromUserId = userId; - message.CreatedDate = DateTime.Now; - message.ToUser = await _context.Users.Where(user => user.Id == message.ToUserId).FirstOrDefaultAsync(); - await _context.ChatMessages.AddAsync(message); - return Ok(await _context.SaveChangesAsync()); -} + private readonly UserManager _userManager; + private readonly ApplicationDbContext _context; + public ChatController(UserManager userManager, ApplicationDbContext context) + { + _userManager = userManager; + _context = context; + } + [HttpGet("{contactId}")] + public Task> GetConversationAsync(string contactId) + { + var userId = User.Claims.Where(a => a.Type == ClaimTypes.NameIdentifier).Select(a => a.Value).FirstOrDefault(); + return _context.ChatMessages + .Where(h => (h.FromUserId == contactId && h.ToUserId == userId) || (h.FromUserId == userId && h.ToUserId == contactId)) + .OrderBy(a => a.CreatedDate) + .Include(a => a.FromUser) + .Include(a => a.ToUser) + .Select(x => new ChatMessage + { + FromUserId = x.FromUserId, + Message = x.Message, + CreatedDate = x.CreatedDate, + Id = x.Id, + ToUserId = x.ToUserId, + Status = x.Status, + ToUser = x.ToUser, + FromUser = x.FromUser + }).ToListAsync(); + } + [HttpGet("users")] + public Task> GetUsersAsync() + { + var userId = User.Claims.Where(a => a.Type == ClaimTypes.NameIdentifier).Select(a => a.Value).FirstOrDefault(); + return _context.Users + .Where(user => user.Id != userId) + .Select(user => new + { + User = user, + UnReadCount = + _context.ChatMessages.Count(msg => msg.FromUserId == user.Id && msg.Status != ChatStatus.Seen) + }) + .ToListAsync() + .OnCompletion(x => x.Select(arg => + { + arg.User.UnreadCount = arg.UnReadCount; + return arg.User; + }) + .ToList()); + } + + [HttpGet("users/{userId}")] + public Task GetUserDetailsAsync(string userId) + => _context.Users + .Where(user => user.Id == userId) + .FirstOrDefaultAsync(); + + [HttpPatch("users/{userId}/status/{status}")] + public async Task UpdateConversationStatus(string userId, ChatStatus status) + { + var messages = await _context.ChatMessages + .Where(x => x.FromUserId == userId && x.Status != ChatStatus.Seen) + .ToListAsync(); + messages.ForEach(x => x.Status = status); + return Ok(await _context.SaveChangesAsync()); + } + [HttpPatch("users/messages/{msgId:long}/status/{status:int}")] + public async Task UpdateMsgStatus(long msgId, int status) + { + + var msg = await _context.ChatMessages.FirstOrDefaultAsync(x => x.Id == msgId); + msg.Status = (ChatStatus) status; + await _context.SaveChangesAsync(); + return msg; + } + [HttpPost] + public async Task SaveMessageAsync(ChatMessage message) + { + var userId = User.Claims.Where(a => a.Type == ClaimTypes.NameIdentifier).Select(a => a.Value).FirstOrDefault(); + message.FromUserId = userId; + message.CreatedDate = DateTime.Now; + var result = await (from user in _context.Users + where user.Id == message.ToUserId + select new + { + ToUser = user, + FromUser = _context.Users.Where(user => user.Id == message.FromUserId).FirstOrDefault(), + }).FirstOrDefaultAsync(); + message.ToUser = result.ToUser; + message.FromUser = result.FromUser; + await _context.ChatMessages.AddAsync(message); + await _context.SaveChangesAsync(); + return message; + } } } diff --git a/BlazorChat/Server/Data/Migrations/20210717181056_AddedChatStatus.Designer.cs b/BlazorChat/Server/Data/Migrations/20210717181056_AddedChatStatus.Designer.cs new file mode 100644 index 0000000..19b3484 --- /dev/null +++ b/BlazorChat/Server/Data/Migrations/20210717181056_AddedChatStatus.Designer.cs @@ -0,0 +1,433 @@ +// +using System; +using BlazorChat.Server.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace BlazorChat.Server.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20210717181056_AddedChatStatus")] + partial class AddedChatStatus + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "6.0.0-preview.6.21352.1") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("BlazorChat.Shared.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("BlazorChat.Shared.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("FromUserId") + .HasColumnType("nvarchar(450)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("ToUserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("FromUserId"); + + b.HasIndex("ToUserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => + { + b.Property("UserCode") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("datetime2"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("UserCode"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.ToTable("DeviceCodes", (string)null); + }); + + modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ConsumedTime") + .HasColumnType("datetime2"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("PersistedGrants", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("BlazorChat.Shared.ChatMessage", b => + { + b.HasOne("BlazorChat.Shared.ApplicationUser", "FromUser") + .WithMany("ChatMessagesFromUsers") + .HasForeignKey("FromUserId"); + + b.HasOne("BlazorChat.Shared.ApplicationUser", "ToUser") + .WithMany("ChatMessagesToUsers") + .HasForeignKey("ToUserId"); + + b.Navigation("FromUser"); + + b.Navigation("ToUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorChat.Shared.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorChat.Shared.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorChat.Shared.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorChat.Shared.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BlazorChat.Shared.ApplicationUser", b => + { + b.Navigation("ChatMessagesFromUsers"); + + b.Navigation("ChatMessagesToUsers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BlazorChat/Server/Data/Migrations/20210717181056_AddedChatStatus.cs b/BlazorChat/Server/Data/Migrations/20210717181056_AddedChatStatus.cs new file mode 100644 index 0000000..b5480f5 --- /dev/null +++ b/BlazorChat/Server/Data/Migrations/20210717181056_AddedChatStatus.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BlazorChat.Server.Data.Migrations +{ + public partial class AddedChatStatus : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Status", + table: "ChatMessages", + type: "int", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "ChatMessages"); + } + } +} diff --git a/BlazorChat/Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/BlazorChat/Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 54defdc..f7d3902 100644 --- a/BlazorChat/Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/BlazorChat/Server/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -16,10 +16,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.3") + .HasAnnotation("ProductVersion", "6.0.0-preview.6.21352.1") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("BlazorChat.Server.Models.ApplicationUser", b => + modelBuilder.Entity("BlazorChat.Shared.ApplicationUser", b => { b.Property("Id") .HasColumnType("nvarchar(450)"); @@ -81,10 +81,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); - b.ToTable("AspNetUsers"); + b.ToTable("AspNetUsers", (string)null); }); - modelBuilder.Entity("BlazorChat.Server.Models.ChatMessage", b => + modelBuilder.Entity("BlazorChat.Shared.ChatMessage", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -100,6 +100,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Message") .HasColumnType("nvarchar(max)"); + b.Property("Status") + .HasColumnType("int"); + b.Property("ToUserId") .HasColumnType("nvarchar(450)"); @@ -159,7 +162,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Expiration"); - b.ToTable("DeviceCodes"); + b.ToTable("DeviceCodes", (string)null); }); modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => @@ -212,7 +215,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("SubjectId", "SessionId", "Type"); - b.ToTable("PersistedGrants"); + b.ToTable("PersistedGrants", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => @@ -239,7 +242,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); - b.ToTable("AspNetRoles"); + b.ToTable("AspNetRoles", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => @@ -263,7 +266,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RoleId"); - b.ToTable("AspNetRoleClaims"); + b.ToTable("AspNetRoleClaims", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => @@ -287,7 +290,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("AspNetUserClaims"); + b.ToTable("AspNetUserClaims", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => @@ -311,7 +314,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("AspNetUserLogins"); + b.ToTable("AspNetUserLogins", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => @@ -326,7 +329,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RoleId"); - b.ToTable("AspNetUserRoles"); + b.ToTable("AspNetUserRoles", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => @@ -347,16 +350,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("UserId", "LoginProvider", "Name"); - b.ToTable("AspNetUserTokens"); + b.ToTable("AspNetUserTokens", (string)null); }); - modelBuilder.Entity("BlazorChat.Server.Models.ChatMessage", b => + modelBuilder.Entity("BlazorChat.Shared.ChatMessage", b => { - b.HasOne("BlazorChat.Server.Models.ApplicationUser", "FromUser") + b.HasOne("BlazorChat.Shared.ApplicationUser", "FromUser") .WithMany("ChatMessagesFromUsers") .HasForeignKey("FromUserId"); - b.HasOne("BlazorChat.Server.Models.ApplicationUser", "ToUser") + b.HasOne("BlazorChat.Shared.ApplicationUser", "ToUser") .WithMany("ChatMessagesToUsers") .HasForeignKey("ToUserId"); @@ -376,7 +379,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { - b.HasOne("BlazorChat.Server.Models.ApplicationUser", null) + b.HasOne("BlazorChat.Shared.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -385,7 +388,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { - b.HasOne("BlazorChat.Server.Models.ApplicationUser", null) + b.HasOne("BlazorChat.Shared.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -400,7 +403,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("BlazorChat.Server.Models.ApplicationUser", null) + b.HasOne("BlazorChat.Shared.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -409,14 +412,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { - b.HasOne("BlazorChat.Server.Models.ApplicationUser", null) + b.HasOne("BlazorChat.Shared.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BlazorChat.Server.Models.ApplicationUser", b => + modelBuilder.Entity("BlazorChat.Shared.ApplicationUser", b => { b.Navigation("ChatMessagesFromUsers"); diff --git a/BlazorChat/Server/Extensions/IdentityServerExtension.cs b/BlazorChat/Server/Extensions/IdentityServerExtension.cs new file mode 100644 index 0000000..11b1192 --- /dev/null +++ b/BlazorChat/Server/Extensions/IdentityServerExtension.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Options; +public class ConfigureJwtBearerOptions : IPostConfigureOptions +{ + public void PostConfigure(string name, JwtBearerOptions options) + { + var originalOnMessageReceived = options.Events.OnMessageReceived; + options.Events.OnMessageReceived = async context => + { + await originalOnMessageReceived(context); + + if (string.IsNullOrEmpty(context.Token)) + { + var accessToken = context.Request.Query["access_token"]; + var path = context.HttpContext.Request.Path; + + if (!string.IsNullOrEmpty(accessToken) && + path.StartsWithSegments("/signalRHub")) + { + context.Token = accessToken; + } + } + }; + } +} \ No newline at end of file diff --git a/BlazorChat/Server/Extensions/SignlrExtensions.cs b/BlazorChat/Server/Extensions/SignlrExtensions.cs new file mode 100644 index 0000000..80bf321 --- /dev/null +++ b/BlazorChat/Server/Extensions/SignlrExtensions.cs @@ -0,0 +1,32 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.SignalR; +using Toolbelt.DynamicBinderExtension; + +namespace BlazorChat.Server.Extensions +{ + public static class SignlrExtensions + { + public static IEnumerable> GetAllConnectedClients(this IHubCallerClients hub) + { + ConcurrentDictionary connections = + hub.All.ToDynamic() + ._lifetimeManager + ._connections + ._connections; + + return connections.AsEnumerable(); + } + public static IEnumerable> GetAllConnectedClients(this IHubCallerClients hub) + { + ConcurrentDictionary connections = + hub.All.ToDynamic() + ._proxy + ._lifetimeManager + ._connections + ._connections; + return connections.AsEnumerable(); + } + } +} diff --git a/BlazorChat/Server/Pages/Error.cshtml.cs b/BlazorChat/Server/Pages/Error.cshtml.cs index da114b9..bc17eaa 100644 --- a/BlazorChat/Server/Pages/Error.cshtml.cs +++ b/BlazorChat/Server/Pages/Error.cshtml.cs @@ -1,11 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; namespace BlazorChat.Server.Pages { diff --git a/BlazorChat/Server/Program.cs b/BlazorChat/Server/Program.cs index 2d3f8a9..dc582be 100644 --- a/BlazorChat/Server/Program.cs +++ b/BlazorChat/Server/Program.cs @@ -1,11 +1,5 @@ using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace BlazorChat.Server { diff --git a/BlazorChat/Server/Properties/launchSettings.json b/BlazorChat/Server/Properties/launchSettings.json index f350f0a..93a006e 100644 --- a/BlazorChat/Server/Properties/launchSettings.json +++ b/BlazorChat/Server/Properties/launchSettings.json @@ -1,30 +1,31 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:48917", - "sslPort": 44373 + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57610", + "sslPort": 44386 + } + }, + "profiles": { + "BlazorChat.Server": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "hotReloadProfile": "aspnetcore", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" } }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "BlazorChat.Server": { - "commandName": "Project", - "dotnetRunMessages": "true", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" } } } +} diff --git a/BlazorChat/Server/SignalRServerHub.cs b/BlazorChat/Server/SignalRServerHub.cs new file mode 100644 index 0000000..93e74b9 --- /dev/null +++ b/BlazorChat/Server/SignalRServerHub.cs @@ -0,0 +1,20 @@ +using BlazorChat.Shared; +using Microsoft.AspNetCore.SignalR; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; + +namespace BlazorChat.Server +{ + [Authorize] + public class SignalRServerHub : Hub, IChatServerHub + { + public Task SendMessageAsync(ChatMessage message, string receiverUserId) + => Clients.User(receiverUserId).ReceiveMessage(message); + public Task ChatNotificationAsync(string message, string receiverUserId, string senderUserId) + => Clients.User(receiverUserId).ReceiveChatNotification(message,senderUserId); + public Task UpdateConversationStatusAsync(string receiverUserId, string senderUserId, ChatStatus status) + => Clients.User(receiverUserId).ReceiveConversationStatus(senderUserId, status); + public Task UpdateMessageStatusAsync(long msgId,string receiverUserId, ChatStatus status) + => Clients.User(receiverUserId).ReceiveMessageStatus(msgId, status); + } +} diff --git a/BlazorChat/Server/Startup.cs b/BlazorChat/Server/Startup.cs index df825da..70eaacc 100644 --- a/BlazorChat/Server/Startup.cs +++ b/BlazorChat/Server/Startup.cs @@ -1,12 +1,16 @@ using BlazorChat.Server.Data; using BlazorChat.Shared; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using System.Text.Json.Serialization; namespace BlazorChat.Server { @@ -24,6 +28,8 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); + // Register the Swagger services + services.AddSwaggerDocument(); services.AddDbContext(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); @@ -39,7 +45,14 @@ public void ConfigureServices(IServiceCollection services) services.AddAuthentication() .AddIdentityServerJwt(); - services.AddControllersWithViews(); + services.TryAddEnumerable( + ServiceDescriptor.Singleton, + ConfigureJwtBearerOptions>()); + + services.AddControllersWithViews().AddJsonOptions(o => { + o.JsonSerializerOptions + .ReferenceHandler = ReferenceHandler.IgnoreCycles; + }); ; services.AddRazorPages(); } @@ -63,6 +76,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); + // Register the Swagger generator and the Swagger UI middlewares + app.UseOpenApi(); + app.UseSwaggerUi3(); + app.UseRouting(); app.UseIdentityServer(); @@ -71,10 +88,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { + + endpoints.MapHub("/signalRHub"); endpoints.MapRazorPages(); endpoints.MapControllers(); endpoints.MapFallbackToFile("index.html"); - endpoints.MapHub("/signalRHub"); }); } } diff --git a/BlazorChat/Server/appsettings.json b/BlazorChat/Server/appsettings.json index 3a2ec57..056247e 100644 --- a/BlazorChat/Server/appsettings.json +++ b/BlazorChat/Server/appsettings.json @@ -4,9 +4,9 @@ }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Trace", + "Microsoft": "Trace", + "Microsoft.Hosting.Lifetime": "Trace" } }, "IdentityServer": { diff --git a/BlazorChat/Shared/ApplicationUser.cs b/BlazorChat/Shared/ApplicationUser.cs index ea34444..701d47e 100644 --- a/BlazorChat/Shared/ApplicationUser.cs +++ b/BlazorChat/Shared/ApplicationUser.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Identity; -using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations.Schema; namespace BlazorChat.Shared { public class ApplicationUser : IdentityUser { + [NotMapped] + public int UnreadCount { get; set; } public virtual ICollection ChatMessagesFromUsers { get; set; } public virtual ICollection ChatMessagesToUsers { get; set; } public ApplicationUser() diff --git a/BlazorChat/Shared/BlazorChat.Shared.csproj b/BlazorChat/Shared/BlazorChat.Shared.csproj index 0fbfd30..d8d6797 100644 --- a/BlazorChat/Shared/BlazorChat.Shared.csproj +++ b/BlazorChat/Shared/BlazorChat.Shared.csproj @@ -1,9 +1,9 @@ - net5.0 + net6.0 - + diff --git a/BlazorChat/Shared/ChatMessage.cs b/BlazorChat/Shared/ChatMessage.cs index beb8eab..a0d9cd9 100644 --- a/BlazorChat/Shared/ChatMessage.cs +++ b/BlazorChat/Shared/ChatMessage.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace BlazorChat.Shared { @@ -11,8 +8,16 @@ public class ChatMessage public string FromUserId { get; set; } public string ToUserId { get; set; } public string Message { get; set; } + public ChatStatus Status { get; set; } public DateTime CreatedDate { get; set; } public virtual ApplicationUser FromUser { get; set; } public virtual ApplicationUser ToUser { get; set; } } + + public enum ChatStatus + { + Undelivered, + Delivered, + Seen + } } diff --git a/BlazorChat/Shared/Extensions/ImmutableExtensions.cs b/BlazorChat/Shared/Extensions/ImmutableExtensions.cs new file mode 100644 index 0000000..792fdb0 --- /dev/null +++ b/BlazorChat/Shared/Extensions/ImmutableExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Immutable; + +namespace BlazorChat.Shared.Extensions +{ + public static class ImmutableExtensions + { + public static ImmutableList Update(this ImmutableList list,Predicate predicate,Action update) + { + var index = list.FindIndex(predicate); + if(index >= 0) + { + var item = list[index]; + update(item); + return list.SetItem(index, item); + } + return list; + } + } +} diff --git a/BlazorChat/Shared/Extensions/StringExtensions.cs b/BlazorChat/Shared/Extensions/StringExtensions.cs new file mode 100644 index 0000000..7a21ec3 --- /dev/null +++ b/BlazorChat/Shared/Extensions/StringExtensions.cs @@ -0,0 +1,8 @@ +namespace BlazorChat.Shared.Extensions +{ + public static class StringExtensions + { + public static bool IsNullEmpty(this string str) + => string.IsNullOrEmpty(str); + } +} diff --git a/BlazorChat/Shared/Extensions/TaskExtensions.cs b/BlazorChat/Shared/Extensions/TaskExtensions.cs new file mode 100644 index 0000000..44db4c2 --- /dev/null +++ b/BlazorChat/Shared/Extensions/TaskExtensions.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; + +namespace BlazorChat.Shared.Extensions +{ + public static class TaskExtensions + { + public static Task OnCompletion(this Task task, Func continuationFunction) + => task.ContinueWith(x => continuationFunction(x.Result), TaskContinuationOptions.OnlyOnRanToCompletion); + public static Task> ToImmutable(this Task> task) + => task.ContinueWith(x => task.Result.ToImmutableList(), TaskContinuationOptions.OnlyOnRanToCompletion); + public static Task> ToImmutable(this Task> task) + => task.ContinueWith(x => task.Result.ToImmutableList(), TaskContinuationOptions.OnlyOnRanToCompletion); + public static Task> ToImmutable(this Task> task) + => task.ContinueWith(x => task.Result.ToImmutableList(), TaskContinuationOptions.OnlyOnRanToCompletion); + } +} diff --git a/BlazorChat/Shared/IChatClientHub.cs b/BlazorChat/Shared/IChatClientHub.cs new file mode 100644 index 0000000..fa110d8 --- /dev/null +++ b/BlazorChat/Shared/IChatClientHub.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace BlazorChat.Shared +{ + public interface IChatClientHub + { + Task ReceiveMessage(ChatMessage message); + Task ReceiveChatNotification(string message, string senderUserId); + Task ReceiveConversationStatus(string userId, ChatStatus status); + Task ReceiveMessageStatus(long msgId,ChatStatus status); + + } +} diff --git a/BlazorChat/Shared/IChatServerHub.cs b/BlazorChat/Shared/IChatServerHub.cs new file mode 100644 index 0000000..7c77832 --- /dev/null +++ b/BlazorChat/Shared/IChatServerHub.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace BlazorChat.Shared +{ + public interface IChatServerHub + { + Task SendMessageAsync(ChatMessage message, string userName); + Task ChatNotificationAsync(string message, string receiverUserId, string senderUserId); + Task UpdateConversationStatusAsync(string receiverUserId, string targetUserId, ChatStatus status); + Task UpdateMessageStatusAsync(long msgId,string receiverUserId, ChatStatus status); + } +} diff --git a/Client/Generated/ChatClient.cs b/Client/Generated/ChatClient.cs new file mode 100644 index 0000000..cb5af1f --- /dev/null +++ b/Client/Generated/ChatClient.cs @@ -0,0 +1,1232 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#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 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?'" + +namespace ChatClientApi +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ChatClient + { + private string _baseUrl = "https://localhost:5001"; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public ChatClient(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + 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); + /// A server side error occurred. + public System.Threading.Tasks.Task GetConversationAsync(string contactId) + { + return GetConversationAsync(contactId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task GetConversationAsync(string contactId, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/{contactId}"); + urlBuilder_.Replace("{contactId}", System.Uri.EscapeDataString(ConvertToString(contactId, System.Globalization.CultureInfo.InvariantCulture))); + + 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("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task GetUsersAsync() + { + return GetUsersAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task GetUsersAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users"); + + 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("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task GetUserDetailsAsync(string userId) + { + return GetUserDetailsAsync(userId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task GetUserDetailsAsync(string userId, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users/{userId}"); + urlBuilder_.Replace("{userId}", System.Uri.EscapeDataString(ConvertToString(userId, System.Globalization.CultureInfo.InvariantCulture))); + + 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("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task UpdateConversationStatusAsync(string userId, ChatStatus status) + { + return UpdateConversationStatusAsync(userId, status, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task UpdateConversationStatusAsync(string userId, ChatStatus status, System.Threading.CancellationToken cancellationToken) + { + if (status == null) + throw new System.ArgumentNullException("status"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users/{userId}/status/{status}"); + urlBuilder_.Replace("{userId}", System.Uri.EscapeDataString(ConvertToString(userId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{status}", System.Uri.EscapeDataString(ConvertToString(status, System.Globalization.CultureInfo.InvariantCulture))); + + 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/octet-stream"); + request_.Method = new System.Net.Http.HttpMethod("PATCH"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task UpdateMsgStatusAsync(long msgId, int status) + { + return UpdateMsgStatusAsync(msgId, status, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task UpdateMsgStatusAsync(long msgId, int status, System.Threading.CancellationToken cancellationToken) + { + if (msgId == null) + throw new System.ArgumentNullException("msgId"); + + if (status == null) + throw new System.ArgumentNullException("status"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat/users/messages/{msgId}/status/{status}"); + urlBuilder_.Replace("{msgId}", System.Uri.EscapeDataString(ConvertToString(msgId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Replace("{status}", System.Uri.EscapeDataString(ConvertToString(status, System.Globalization.CultureInfo.InvariantCulture))); + + 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("PATCH"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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(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 + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("A server side error occurred.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + 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(); + } + } + + /// A server side error occurred. + public System.Threading.Tasks.Task SaveMessageAsync(ChatMessage message) + { + return SaveMessageAsync(message, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task SaveMessageAsync(ChatMessage message, System.Threading.CancellationToken cancellationToken) + { + if (message == null) + throw new System.ArgumentNullException("message"); + + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Chat"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(System.Text.Json.JsonSerializer.Serialize(message, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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 + { + 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> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.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)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.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.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class OidcConfigurationClient + { + private string _baseUrl = "https://localhost:5001"; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public OidcConfigurationClient(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + 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); + /// A server side error occurred. + public System.Threading.Tasks.Task GetClientRequestParametersAsync(string clientId) + { + return GetClientRequestParametersAsync(clientId, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task GetClientRequestParametersAsync(string clientId, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/_configuration/{clientId}"); + urlBuilder_.Replace("{clientId}", System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))); + + 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("application/octet-stream")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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 || status_ == 206) + { + var responseStream_ = response_.Content == null ? System.IO.Stream.Null : await response_.Content.ReadAsStreamAsync().ConfigureAwait(false); + var fileResponse_ = new FileResponse(status_, headers_, responseStream_, null, response_); + disposeClient_ = false; disposeResponse_ = false; // response and client are disposed by FileResponse + return fileResponse_; + } + 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 + { + 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> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.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)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.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.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class WeatherForecastClient + { + private string _baseUrl = "https://localhost:5001"; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public WeatherForecastClient(System.Net.Http.HttpClient httpClient) + { + _httpClient = httpClient; + _settings = new System.Lazy(CreateSerializerSettings); + } + + private System.Text.Json.JsonSerializerOptions CreateSerializerSettings() + { + var settings = new System.Text.Json.JsonSerializerOptions(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected System.Text.Json.JsonSerializerOptions JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings); + + + 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); + /// A server side error occurred. + public System.Threading.Tasks.Task> GetAsync() + { + return GetAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetAsync(System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/WeatherForecast"); + + 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("application/json")); + + 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_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.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>(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(); + } + } + + protected struct ObjectResponseResult + { + 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> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = System.Text.Json.JsonSerializer.Deserialize(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (System.Text.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)) + { + var typedBody = await System.Text.Json.JsonSerializer.DeserializeAsync(responseStream, JsonSerializerSettings, cancellationToken).ConfigureAwait(false); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (System.Text.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.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public enum ChatStatus + { + Undelivered = 0, + + Delivered = 1, + + Seen = 2, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ChatMessage + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public long Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("fromUserId")] + public string FromUserId { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("toUserId")] + public string ToUserId { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("message")] + public string Message { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("status")] + public ChatStatus Status { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("createdDate")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset CreatedDate { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("fromUser")] + public ApplicationUser FromUser { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("toUser")] + public ApplicationUser ToUser { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class ApplicationUser : IdentityUser + { + [System.Text.Json.Serialization.JsonPropertyName("lastSeen")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset LastSeen { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("unreadCount")] + public int UnreadCount { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("chatMessagesFromUsers")] + public System.Collections.Generic.ICollection ChatMessagesFromUsers { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("chatMessagesToUsers")] + public System.Collections.Generic.ICollection ChatMessagesToUsers { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class IdentityUser : IdentityUserOfString + { + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class IdentityUserOfString + { + [System.Text.Json.Serialization.JsonPropertyName("id")] + public string Id { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("userName")] + public string UserName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("normalizedUserName")] + public string NormalizedUserName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("email")] + public string Email { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("normalizedEmail")] + public string NormalizedEmail { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("emailConfirmed")] + public bool EmailConfirmed { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("passwordHash")] + public string PasswordHash { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("securityStamp")] + public string SecurityStamp { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("concurrencyStamp")] + public string ConcurrencyStamp { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("phoneNumber")] + public string PhoneNumber { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("phoneNumberConfirmed")] + public bool PhoneNumberConfirmed { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("twoFactorEnabled")] + public bool TwoFactorEnabled { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("lockoutEnd")] + public System.DateTimeOffset? LockoutEnd { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("lockoutEnabled")] + public bool LockoutEnabled { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("accessFailedCount")] + public int AccessFailedCount { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.4.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class WeatherForecast + { + [System.Text.Json.Serialization.JsonPropertyName("date")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset Date { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("temperatureC")] + public int TemperatureC { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("summary")] + public string Summary { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("temperatureF")] + public int TemperatureF { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class FileResponse : System.IDisposable + { + private System.IDisposable _client; + private System.IDisposable _response; + + public int StatusCode { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public System.IO.Stream Stream { get; private set; } + + public bool IsPartial + { + get { return StatusCode == 206; } + } + + public FileResponse(int statusCode, System.Collections.Generic.IReadOnlyDictionary> headers, System.IO.Stream stream, System.IDisposable client, System.IDisposable response) + { + StatusCode = statusCode; + Headers = headers; + Stream = stream; + _client = client; + _response = response; + } + + public void Dispose() + { + Stream.Dispose(); + if (_response != null) + _response.Dispose(); + if (_client != null) + _client.Dispose(); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.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> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> 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", "13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 \ No newline at end of file