Контракт 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>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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();
|
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)
|
if (quantity <= 0)
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ public sealed class Order
|
|||||||
|
|
||||||
_items.Add(new OrderItem
|
_items.Add(new OrderItem
|
||||||
{
|
{
|
||||||
MenuItemId = menuItemId,
|
Id = id,
|
||||||
Quantity = quantity,
|
Quantity = quantity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public sealed class OrderItem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Идентификатор блюда на сервере (<see cref="Dish.Id"/>).
|
/// Идентификатор блюда на сервере (<see cref="Dish.Id"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required string MenuItemId { get; init; }
|
public required string Id { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Количество. Для весовых блюд допускаются дробные значения.
|
/// Количество. Для весовых блюд допускаются дробные значения.
|
||||||
|
|||||||
Reference in New Issue
Block a user