408 lines
14 KiB
C#
408 lines
14 KiB
C#
using Fitness_core_BE.DTOs.Auth;
|
|
using System.Text.Json;
|
|
using Fitness_core_BE.Exceptions;
|
|
|
|
namespace Fitness_core_BE.Helpers
|
|
{
|
|
public class DataExtractor
|
|
{
|
|
public static string GetRequiredString(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new BadRequestException($"Missing required field '{key}'.");
|
|
|
|
if (element.ValueKind != JsonValueKind.String)
|
|
throw new BadRequestException($"Field '{key}' must be a string.");
|
|
|
|
var value = element.GetString();
|
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
return "";
|
|
//throw new BadRequestException($"Field '{key}' cannot be empty.");
|
|
|
|
|
|
return value;
|
|
}
|
|
|
|
public static string? GetOptionalString(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.ValueKind == JsonValueKind.String)
|
|
{
|
|
var value = element.GetString();
|
|
return string.IsNullOrWhiteSpace(value) ? null : value;
|
|
}
|
|
|
|
if (element.ValueKind == JsonValueKind.Number)
|
|
return element.GetRawText();
|
|
|
|
throw new ArgumentException($"Field '{key}' must be a string.");
|
|
}
|
|
|
|
public static string? GetOptionalStringByKeys(
|
|
Dictionary<string, JsonElement> data,
|
|
params string[] keys)
|
|
{
|
|
foreach (var key in keys)
|
|
{
|
|
if (data.ContainsKey(key))
|
|
return GetOptionalString(data, key);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string GetRequiredStringByKeys(
|
|
Dictionary<string, JsonElement> data,
|
|
params string[] keys)
|
|
{
|
|
var value = GetOptionalStringByKeys(data, keys);
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
return value;
|
|
|
|
throw new BadRequestException($"Missing required field '{keys[0]}'.");
|
|
}
|
|
|
|
// ===================== INT =====================
|
|
|
|
public static int GetRequiredInt(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new BadRequestException($"Missing required field '{key}'.");
|
|
|
|
if (element.TryGetInt32(out var value))
|
|
return value;
|
|
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
int.TryParse(element.GetString(), out value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid integer.");
|
|
}
|
|
|
|
public static int? GetNullableInt(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.TryGetInt32(out var value))
|
|
return value;
|
|
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
int.TryParse(element.GetString(), out value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid integer.");
|
|
}
|
|
|
|
// ===================== FLOAT =====================
|
|
|
|
public static float GetRequiredFloat(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new BadRequestException($"Missing required field '{key}'.");
|
|
|
|
if (element.TryGetSingle(out var value))
|
|
return value;
|
|
|
|
// Allow numeric strings: "12.5"
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
float.TryParse(element.GetString(), out value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid float.");
|
|
}
|
|
|
|
// ===================== DECIMAL =====================
|
|
public static decimal GetRequiredDecimal(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new ArgumentException($"Missing required field '{key}'.");
|
|
if (element.TryGetDecimal(out var value))
|
|
return value;
|
|
// Allow numeric strings: "12.5"
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
decimal.TryParse(element.GetString(), out value))
|
|
{
|
|
return value;
|
|
}
|
|
throw new ArgumentException($"Field '{key}' must be a valid decimal.");
|
|
}
|
|
|
|
public static decimal? GetOptionalDecimal(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.TryGetDecimal(out var value))
|
|
return value;
|
|
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
decimal.TryParse(element.GetString(), out value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
throw new ArgumentException($"Field '{key}' must be a valid decimal.");
|
|
}
|
|
|
|
public static decimal? GetOptionalDecimalByKeys(
|
|
Dictionary<string, JsonElement> data,
|
|
params string[] keys)
|
|
{
|
|
foreach (var key in keys)
|
|
{
|
|
if (data.ContainsKey(key))
|
|
return GetOptionalDecimal(data, key);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// ===================== BOOLEAN =====================
|
|
|
|
public static bool GetRequiredBool(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new BadRequestException($"Missing required field '{key}'.");
|
|
|
|
if (element.ValueKind == JsonValueKind.True) return true;
|
|
if (element.ValueKind == JsonValueKind.False) return false;
|
|
|
|
// Allow string values: "true" / "false"
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
bool.TryParse(element.GetString(), out var value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid boolean.");
|
|
}
|
|
|
|
public static bool? GetOptionalBool(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.ValueKind == JsonValueKind.True) return true;
|
|
if (element.ValueKind == JsonValueKind.False) return false;
|
|
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
bool.TryParse(element.GetString(), out var value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid boolean.");
|
|
}
|
|
|
|
// ===================== GUID =====================
|
|
|
|
public static Guid GetRequiredGuid(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
var value = GetRequiredString(data, key);
|
|
|
|
if (!Guid.TryParse(value, out var guid))
|
|
throw new BadRequestException($"Field '{key}' must be a valid GUID.");
|
|
|
|
return guid;
|
|
}
|
|
|
|
/// <summary>A JSON array of GUID strings, or null if the key is absent/null. An
|
|
/// empty array is returned as an empty list (distinct from "field not sent"),
|
|
/// which matters for "clear this plan's places" updates.</summary>
|
|
public static List<Guid>? GetOptionalGuidList(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.ValueKind != JsonValueKind.Array)
|
|
throw new BadRequestException($"Field '{key}' must be an array.");
|
|
|
|
var result = new List<Guid>();
|
|
foreach (var item in element.EnumerateArray())
|
|
{
|
|
if (item.ValueKind != JsonValueKind.String || !Guid.TryParse(item.GetString(), out var guid))
|
|
throw new BadRequestException($"Field '{key}' must contain only GUID strings.");
|
|
result.Add(guid);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ===================== DATETIME =====================
|
|
|
|
public static DateTime GetRequiredDateTime(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new BadRequestException($"Missing required field '{key}'.");
|
|
|
|
if (element.ValueKind == JsonValueKind.Null)
|
|
throw new BadRequestException($"Field '{key}' cannot be null.");
|
|
|
|
// ISO 8601 string
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
DateTime.TryParse(element.GetString(), out var date))
|
|
{
|
|
return date;
|
|
}
|
|
|
|
// Native JSON DateTime
|
|
if (element.TryGetDateTime(out date))
|
|
{
|
|
return date;
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid DateTime.");
|
|
}
|
|
|
|
public static DateTime? GetOptionalDateTime(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
DateTime.TryParse(element.GetString(), out var date))
|
|
{
|
|
return date;
|
|
}
|
|
|
|
if (element.TryGetDateTime(out date))
|
|
return date;
|
|
|
|
throw new ArgumentException($"Field '{key}' must be a valid DateTime.");
|
|
}
|
|
|
|
// ===================== DATEONLY =====================
|
|
|
|
public static DateOnly GetRequiredDateOnly(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element))
|
|
throw new BadRequestException($"Missing required field '{key}'.");
|
|
|
|
if (element.ValueKind == JsonValueKind.Null)
|
|
throw new BadRequestException($"Field '{key}' cannot be null.");
|
|
|
|
DateTime date;
|
|
|
|
// ISO 8601 string (e.g., "2025-02-25")
|
|
if (element.ValueKind == JsonValueKind.String &&
|
|
DateTime.TryParse(element.GetString(), out date))
|
|
{
|
|
return DateOnly.FromDateTime(date);
|
|
}
|
|
|
|
|
|
if (element.TryGetDateTime(out date))
|
|
{
|
|
return DateOnly.FromDateTime(date);
|
|
}
|
|
|
|
throw new BadRequestException($"Field '{key}' must be a valid date.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// A date of birth, rejected if it is today or in the future. Progress predictions
|
|
/// divide by the member's age, so a zero age has to be stopped at the write rather
|
|
/// than surfacing later as a failure in an unrelated screen.
|
|
/// </summary>
|
|
public static DateOnly GetRequiredDateOfBirth(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
var birthDate = GetRequiredDateOnly(data, key);
|
|
|
|
if (birthDate >= DateOnly.FromDateTime(DateTime.UtcNow))
|
|
throw new BadRequestException(
|
|
$"Field '{key}' must be a date in the past.");
|
|
|
|
return birthDate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Same as GetRequiredDateOfBirth, but missing/null/blank is allowed and returns null
|
|
/// instead of throwing -- a birthday isn't needed to run a subscription, so gyms that
|
|
/// don't collect it at signup shouldn't be blocked from adding a member.
|
|
/// </summary>
|
|
public static DateOnly? GetOptionalDateOfBirth(
|
|
Dictionary<string, JsonElement> data,
|
|
string key)
|
|
{
|
|
if (!data.TryGetValue(key, out var element) || element.ValueKind == JsonValueKind.Null)
|
|
return null;
|
|
|
|
if (element.ValueKind == JsonValueKind.String && string.IsNullOrWhiteSpace(element.GetString()))
|
|
return null;
|
|
|
|
return GetRequiredDateOfBirth(data, key);
|
|
}
|
|
|
|
// ===================== USER ID FROM HttpContext =====================
|
|
public static Guid GetUserIdFromContext(HttpContext httpContext)
|
|
{
|
|
|
|
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
|
|
|
|
var userIdFromContext = httpContext.Items["UserId"]?.ToString()
|
|
?? httpContext.User?.FindFirst("UserId")?.Value;
|
|
|
|
if (string.IsNullOrWhiteSpace(userIdFromContext) || !Guid.TryParse(userIdFromContext, out var userId))
|
|
{
|
|
throw new UnauthorizedAccessException("User is not authenticated. Please login and send cookie.");
|
|
}
|
|
|
|
return userId;
|
|
}
|
|
|
|
//============================AuthResponce EXtractor============
|
|
public static AuthResponse GetRequiredAuthResponse(Dictionary<string, JsonElement> data)
|
|
{
|
|
AuthResponse authResponse = new AuthResponse
|
|
{
|
|
StatusCode = GetRequiredInt(data, "StatusCode"),
|
|
Success = GetRequiredBool(data, "success"),
|
|
Message = GetRequiredString(data, "message"),
|
|
Data = data
|
|
};
|
|
|
|
return authResponse;
|
|
}
|
|
}
|
|
}
|