From 9c5763ba3848d2fdf8c969a3516cb2e50fffd2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=8B=D1=82=D0=BA=D0=BE=D0=B2=20=D0=A0=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD?= Date: Mon, 1 Jun 2026 00:41:06 +0300 Subject: [PATCH] ApiClient --- src/ApiClient/Class1.cs | 6 -- src/ApiClient/Grpc/GrpcSmsClient.cs | 71 +++++++++++++++++ src/ApiClient/Grpc/GrpcSmsClientOptions.cs | 6 ++ src/ApiClient/Http/HttpSmsClient.cs | 89 ++++++++++++++++++++++ src/ApiClient/Http/HttpSmsClientOptions.cs | 10 +++ src/ApiClient/ISmsClient.cs | 11 +++ src/ApiClient/SmsClientFactory.cs | 11 +++ 7 files changed, 198 insertions(+), 6 deletions(-) delete mode 100644 src/ApiClient/Class1.cs create mode 100644 src/ApiClient/Grpc/GrpcSmsClient.cs create mode 100644 src/ApiClient/Grpc/GrpcSmsClientOptions.cs create mode 100644 src/ApiClient/Http/HttpSmsClient.cs create mode 100644 src/ApiClient/Http/HttpSmsClientOptions.cs create mode 100644 src/ApiClient/ISmsClient.cs create mode 100644 src/ApiClient/SmsClientFactory.cs diff --git a/src/ApiClient/Class1.cs b/src/ApiClient/Class1.cs deleted file mode 100644 index 95666be..0000000 --- a/src/ApiClient/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace ApiClient; - -public class Class1 -{ - -} diff --git a/src/ApiClient/Grpc/GrpcSmsClient.cs b/src/ApiClient/Grpc/GrpcSmsClient.cs new file mode 100644 index 0000000..4f50393 --- /dev/null +++ b/src/ApiClient/Grpc/GrpcSmsClient.cs @@ -0,0 +1,71 @@ +using Contracts; +using Contracts.Menu; +using Contracts.Responses; +using Domain.Entities; +using Google.Protobuf.WellKnownTypes; +using Grpc.Net.Client; +using Sms.Test; + +namespace ApiClient.Grpc; + +public sealed class GrpcSmsClient : ISmsClient, IDisposable +{ + private readonly GrpcChannel _channel; + private readonly SmsTestService.SmsTestServiceClient _client; + + public GrpcSmsClient(GrpcSmsClientOptions options) + { + _channel = GrpcChannel.ForAddress(options.Address); + _client = new SmsTestService.SmsTestServiceClient(_channel); + } + + public async Task GetMenuAsync(bool withPrice = true, CancellationToken cancellationToken = default) + { + var response = await _client.GetMenuAsync(new BoolValue { Value = withPrice }, cancellationToken: cancellationToken); + + return new GetMenuApiResponse + { + Command = Commands.GetMenu, + Success = response.Success, + ErrorMessage = response.ErrorMessage, + Data = response.Success + ? new GetMenuData { MenuItems = response.MenuItems.Select(ToDish).ToList() } + : null, + }; + } + + public async Task SendOrderAsync(Domain.Entities.Order order, CancellationToken cancellationToken = default) + { + var grpcOrder = new Sms.Test.Order { Id = order.Id.ToString() }; + grpcOrder.OrderItems.AddRange(order.Items.Select(item => new Sms.Test.OrderItem + { + Id = item.Id, + Quantity = (double)item.Quantity, + })); + + var response = await _client.SendOrderAsync(grpcOrder, cancellationToken: cancellationToken); + + return new SendOrderApiResponse + { + Command = Commands.SendOrder, + Success = response.Success, + ErrorMessage = response.ErrorMessage, + }; + } + + public void Dispose() + { + _channel.Dispose(); + } + + private static Dish ToDish(MenuItem item) => new() + { + Id = item.Id, + Article = item.Article, + Name = item.Name, + Price = (decimal)item.Price, + IsWeighted = item.IsWeighted, + FullPath = item.FullPath, + Barcodes = item.Barcodes.ToList(), + }; +} diff --git a/src/ApiClient/Grpc/GrpcSmsClientOptions.cs b/src/ApiClient/Grpc/GrpcSmsClientOptions.cs new file mode 100644 index 0000000..0d4243a --- /dev/null +++ b/src/ApiClient/Grpc/GrpcSmsClientOptions.cs @@ -0,0 +1,6 @@ +namespace ApiClient.Grpc; + +public sealed class GrpcSmsClientOptions +{ + public string Address { get; set; } = "http://localhost:5053"; +} diff --git a/src/ApiClient/Http/HttpSmsClient.cs b/src/ApiClient/Http/HttpSmsClient.cs new file mode 100644 index 0000000..26c2193 --- /dev/null +++ b/src/ApiClient/Http/HttpSmsClient.cs @@ -0,0 +1,89 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using Contracts; +using Contracts.Menu; +using Contracts.Orders; +using Contracts.Requests; +using Contracts.Responses; +using Domain.Entities; + +namespace ApiClient.Http; + +public sealed class HttpSmsClient : ISmsClient, IDisposable +{ + private readonly HttpClient _httpClient; + + public HttpSmsClient(HttpSmsClientOptions options) + { + _httpClient = new HttpClient { BaseAddress = new Uri(options.BaseUrl) }; + + var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{options.Username}:{options.Password}")); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials); + } + + public async Task GetMenuAsync(bool withPrice = true, CancellationToken cancellationToken = default) + { + var request = new GetMenuApiRequest + { + CommandParameters = new GetMenuParameters { WithPrice = withPrice }, + }; + + var response = await SendAsync(request, cancellationToken); + return ToGetMenuResponse(response); + } + + public async Task SendOrderAsync(Order order, CancellationToken cancellationToken = default) + { + var request = new SendOrderApiRequest + { + CommandParameters = new SendOrderParameters + { + OrderId = order.Id.ToString(), + MenuItems = order.Items.ToList(), + }, + }; + + var response = await SendAsync(request, cancellationToken); + return ToSendOrderResponse(response); + } + + public void Dispose() => _httpClient.Dispose(); + + private async Task SendAsync(ApiRequest request, CancellationToken cancellationToken) + { + var json = JsonSerializer.Serialize(request, ApiJsonOptions.Instance); + using var content = new StringContent(json, Encoding.UTF8, "application/json"); + using var httpResponse = await _httpClient.PostAsync("/", content, cancellationToken); + + if (httpResponse.StatusCode == HttpStatusCode.Unauthorized) + { + return new ApiResponse + { + Command = request.Command, + Success = false, + ErrorMessage = "Требуется аутентификация.", + }; + } + + var responseJson = await httpResponse.Content.ReadAsStringAsync(cancellationToken); + return ApiResponseDeserializer.Deserialize(responseJson); + } + + private static GetMenuApiResponse ToGetMenuResponse(ApiResponse response) => + response as GetMenuApiResponse ?? new GetMenuApiResponse + { + Command = Commands.GetMenu, + Success = false, + ErrorMessage = response.ErrorMessage, + }; + + private static SendOrderApiResponse ToSendOrderResponse(ApiResponse response) => + response as SendOrderApiResponse ?? new SendOrderApiResponse + { + Command = Commands.SendOrder, + Success = false, + ErrorMessage = response.ErrorMessage, + }; +} diff --git a/src/ApiClient/Http/HttpSmsClientOptions.cs b/src/ApiClient/Http/HttpSmsClientOptions.cs new file mode 100644 index 0000000..d747755 --- /dev/null +++ b/src/ApiClient/Http/HttpSmsClientOptions.cs @@ -0,0 +1,10 @@ +namespace ApiClient.Http; + +public sealed class HttpSmsClientOptions +{ + public string BaseUrl { get; set; } = "http://localhost:5053"; + + public string Username { get; set; } = "user"; + + public string Password { get; set; } = "password"; +} diff --git a/src/ApiClient/ISmsClient.cs b/src/ApiClient/ISmsClient.cs new file mode 100644 index 0000000..49cf720 --- /dev/null +++ b/src/ApiClient/ISmsClient.cs @@ -0,0 +1,11 @@ +using Contracts.Responses; +using Domain.Entities; + +namespace ApiClient; + +public interface ISmsClient +{ + Task GetMenuAsync(bool withPrice = true, CancellationToken cancellationToken = default); + + Task SendOrderAsync(Order order, CancellationToken cancellationToken = default); +} diff --git a/src/ApiClient/SmsClientFactory.cs b/src/ApiClient/SmsClientFactory.cs new file mode 100644 index 0000000..31dd6ab --- /dev/null +++ b/src/ApiClient/SmsClientFactory.cs @@ -0,0 +1,11 @@ +using ApiClient.Grpc; +using ApiClient.Http; + +namespace ApiClient; + +public static class SmsClientFactory +{ + public static HttpSmsClient CreateHttp(HttpSmsClientOptions options) => new(options); + + public static GrpcSmsClient CreateGrpc(GrpcSmsClientOptions options) => new(options); +}