Контракт http
This commit is contained in:
20
src/Contracts/ApiJsonOptions.cs
Normal file
20
src/Contracts/ApiJsonOptions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Text.Json;
|
||||
using Contracts.Json;
|
||||
|
||||
namespace Contracts;
|
||||
|
||||
public static class ApiJsonOptions
|
||||
{
|
||||
public static JsonSerializerOptions Instance { get; } = Create();
|
||||
|
||||
private static JsonSerializerOptions Create()
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = null,
|
||||
};
|
||||
|
||||
options.Converters.Add(new DecimalFromStringJsonConverter());
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Contracts;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
7
src/Contracts/Commands.cs
Normal file
7
src/Contracts/Commands.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Contracts;
|
||||
|
||||
public static class Commands
|
||||
{
|
||||
public const string GetMenu = "GetMenu";
|
||||
public const string SendOrder = "SendOrder";
|
||||
}
|
||||
@@ -6,4 +6,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
24
src/Contracts/Json/DecimalFromStringJsonConverter.cs
Normal file
24
src/Contracts/Json/DecimalFromStringJsonConverter.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Contracts.Json;
|
||||
|
||||
public sealed class DecimalFromStringJsonConverter : JsonConverter<decimal>
|
||||
{
|
||||
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.String => decimal.Parse(
|
||||
reader.GetString()!,
|
||||
NumberStyles.Number,
|
||||
CultureInfo.InvariantCulture),
|
||||
JsonTokenType.Number => reader.GetDecimal(),
|
||||
_ => throw new JsonException($"Unexpected token {reader.TokenType} when parsing decimal."),
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) =>
|
||||
writer.WriteNumberValue(value);
|
||||
}
|
||||
8
src/Contracts/Menu/GetMenuData.cs
Normal file
8
src/Contracts/Menu/GetMenuData.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Domain.Entities;
|
||||
|
||||
namespace Contracts.Menu;
|
||||
|
||||
public sealed class GetMenuData
|
||||
{
|
||||
public IReadOnlyList<Dish> MenuItems { get; init; } = [];
|
||||
}
|
||||
6
src/Contracts/Menu/GetMenuParameters.cs
Normal file
6
src/Contracts/Menu/GetMenuParameters.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Contracts.Menu;
|
||||
|
||||
public sealed class GetMenuParameters
|
||||
{
|
||||
public bool WithPrice { get; set; } = true;
|
||||
}
|
||||
10
src/Contracts/Orders/SendOrderParameters.cs
Normal file
10
src/Contracts/Orders/SendOrderParameters.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Domain.Entities;
|
||||
|
||||
namespace Contracts.Orders;
|
||||
|
||||
public sealed class SendOrderParameters
|
||||
{
|
||||
public required string OrderId { get; init; }
|
||||
|
||||
public IReadOnlyList<OrderItem> MenuItems { get; init; } = [];
|
||||
}
|
||||
11
src/Contracts/Requests/ApiRequest.cs
Normal file
11
src/Contracts/Requests/ApiRequest.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Contracts.Requests;
|
||||
|
||||
public class ApiRequest
|
||||
{
|
||||
public string Command { get; set; } = "";
|
||||
}
|
||||
|
||||
public class ApiRequest<TParameters> : ApiRequest
|
||||
{
|
||||
public TParameters? CommandParameters { get; set; }
|
||||
}
|
||||
33
src/Contracts/Requests/ApiRequestDeserializer.cs
Normal file
33
src/Contracts/Requests/ApiRequestDeserializer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Contracts.Requests;
|
||||
|
||||
public static class ApiRequestDeserializer
|
||||
{
|
||||
public static ApiRequest Deserialize(string json) =>
|
||||
Deserialize(JsonDocument.Parse(json).RootElement);
|
||||
|
||||
public static ApiRequest Deserialize(ReadOnlySpan<byte> utf8Json) =>
|
||||
Deserialize(JsonSerializer.Deserialize<JsonElement>(utf8Json, ApiJsonOptions.Instance)!);
|
||||
|
||||
public static async Task<ApiRequest> DeserializeAsync(Stream stream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
|
||||
return Deserialize(document.RootElement);
|
||||
}
|
||||
|
||||
public static ApiRequest Deserialize(JsonElement json)
|
||||
{
|
||||
if (!json.TryGetProperty("Command", out var commandElement))
|
||||
{
|
||||
return json.Deserialize<ApiRequest>(ApiJsonOptions.Instance)!;
|
||||
}
|
||||
|
||||
return commandElement.GetString() switch
|
||||
{
|
||||
Commands.GetMenu => json.Deserialize<GetMenuApiRequest>(ApiJsonOptions.Instance)!,
|
||||
Commands.SendOrder => json.Deserialize<SendOrderApiRequest>(ApiJsonOptions.Instance)!,
|
||||
_ => json.Deserialize<ApiRequest>(ApiJsonOptions.Instance)!,
|
||||
};
|
||||
}
|
||||
}
|
||||
11
src/Contracts/Requests/GetMenuApiRequest.cs
Normal file
11
src/Contracts/Requests/GetMenuApiRequest.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Contracts.Menu;
|
||||
|
||||
namespace Contracts.Requests;
|
||||
|
||||
public sealed class GetMenuApiRequest : ApiRequest<GetMenuParameters>
|
||||
{
|
||||
public GetMenuApiRequest()
|
||||
{
|
||||
Command = Commands.GetMenu;
|
||||
}
|
||||
}
|
||||
11
src/Contracts/Requests/SendOrderApiRequest.cs
Normal file
11
src/Contracts/Requests/SendOrderApiRequest.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Contracts.Orders;
|
||||
|
||||
namespace Contracts.Requests;
|
||||
|
||||
public sealed class SendOrderApiRequest : ApiRequest<SendOrderParameters>
|
||||
{
|
||||
public SendOrderApiRequest()
|
||||
{
|
||||
Command = Commands.SendOrder;
|
||||
}
|
||||
}
|
||||
10
src/Contracts/Responses/ApiResponse.cs
Normal file
10
src/Contracts/Responses/ApiResponse.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Contracts.Responses;
|
||||
|
||||
public class ApiResponse
|
||||
{
|
||||
public required string Command { get; init; }
|
||||
|
||||
public bool Success { get; init; }
|
||||
|
||||
public string ErrorMessage { get; init; } = "";
|
||||
}
|
||||
33
src/Contracts/Responses/ApiResponseDeserializer.cs
Normal file
33
src/Contracts/Responses/ApiResponseDeserializer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Contracts.Responses;
|
||||
|
||||
public static class ApiResponseDeserializer
|
||||
{
|
||||
public static ApiResponse Deserialize(string json) =>
|
||||
Deserialize(JsonDocument.Parse(json).RootElement);
|
||||
|
||||
public static ApiResponse Deserialize(ReadOnlySpan<byte> utf8Json) =>
|
||||
Deserialize(JsonSerializer.Deserialize<JsonElement>(utf8Json, ApiJsonOptions.Instance)!);
|
||||
|
||||
public static async Task<ApiResponse> DeserializeAsync(Stream stream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
|
||||
return Deserialize(document.RootElement);
|
||||
}
|
||||
|
||||
public static ApiResponse Deserialize(JsonElement json)
|
||||
{
|
||||
if (!json.TryGetProperty("Command", out var commandElement))
|
||||
{
|
||||
return json.Deserialize<ApiResponse>(ApiJsonOptions.Instance)!;
|
||||
}
|
||||
|
||||
return commandElement.GetString() switch
|
||||
{
|
||||
Commands.GetMenu => json.Deserialize<GetMenuApiResponse>(ApiJsonOptions.Instance)!,
|
||||
Commands.SendOrder => json.Deserialize<SendOrderApiResponse>(ApiJsonOptions.Instance)!,
|
||||
_ => json.Deserialize<ApiResponse>(ApiJsonOptions.Instance)!,
|
||||
};
|
||||
}
|
||||
}
|
||||
8
src/Contracts/Responses/GetMenuApiResponse.cs
Normal file
8
src/Contracts/Responses/GetMenuApiResponse.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Contracts.Menu;
|
||||
|
||||
namespace Contracts.Responses;
|
||||
|
||||
public sealed class GetMenuApiResponse : ApiResponse
|
||||
{
|
||||
public GetMenuData? Data { get; init; }
|
||||
}
|
||||
4
src/Contracts/Responses/SendOrderApiResponse.cs
Normal file
4
src/Contracts/Responses/SendOrderApiResponse.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace Contracts.Responses;
|
||||
|
||||
public sealed class SendOrderApiResponse : ApiResponse;
|
||||
|
||||
@@ -16,9 +16,9 @@ public sealed class Order
|
||||
Id = id ?? Guid.NewGuid();
|
||||
}
|
||||
|
||||
public void AddItem(string menuItemId, decimal quantity)
|
||||
public void AddItem(string id, decimal quantity)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(menuItemId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(id);
|
||||
|
||||
if (quantity <= 0)
|
||||
{
|
||||
@@ -30,7 +30,7 @@ public sealed class Order
|
||||
|
||||
_items.Add(new OrderItem
|
||||
{
|
||||
MenuItemId = menuItemId,
|
||||
Id = id,
|
||||
Quantity = quantity,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public sealed class OrderItem
|
||||
/// <summary>
|
||||
/// Идентификатор блюда на сервере (<see cref="Dish.Id"/>).
|
||||
/// </summary>
|
||||
public required string MenuItemId { get; init; }
|
||||
public required string Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Количество. Для весовых блюд допускаются дробные значения.
|
||||
|
||||
Reference in New Issue
Block a user