RobotNet/RobotNet.ScriptManager/Models/ScriptMapManager.cs
2025-10-15 15:15:53 +07:00

320 lines
12 KiB
C#

using OpenIddict.Client;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Models;
using RobotNet.Script;
using RobotNet.Shares;
using Serialize.Linq.Serializers;
using System.Linq.Expressions;
using System.Net.Http.Headers;
namespace RobotNet.ScriptManager.Models;
public class ScriptMapManager(OpenIddictClientService openIddictClient, string MapManagerUrl, string[] MapManagerScopes) : IMapManager
{
private string? CachedToken;
private DateTime TokenExpiry;
private static readonly ExpressionSerializer expressionSerializer = new(new Serialize.Linq.Serializers.JsonSerializer());
public Task<IElement[]> FindElements(string map, string model)
=> FindElements(map, model, element => true);
public Task<IElement[]> FindElements(string map, string model, Expression<Func<Script.Expressions.ElementProperties, bool>> expr)
=> FindElements(map, model, expr, true);
private async Task<IElement[]> FindElements(string map, string model, Expression<Func<Script.Expressions.ElementProperties, bool>> expr, bool retry)
{
var accessToken = await RequestAccessToken();
using var client = new HttpClient()
{
BaseAddress = new Uri(MapManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Post, $"api/ScriptElements");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Đưa modelExpression vào body của request
request.Content = JsonContent.Create(new ElementExpressionModel
{
MapName = map,
ModelName = model,
Expression = expressionSerializer.SerializeText(expr),
});
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
CachedToken = null; // Clear cached token to force re-authentication
return await FindElements(map, model, expr, false); // Retry without recall
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate.");
}
}
else if (status.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to get elements: {status.ReasonPhrase}");
}
else
{
var result = await response.Content.ReadFromJsonAsync<MessageResult<IEnumerable<ElementDto>>>();
if (result == null)
{
throw new InvalidOperationException("Failed to deserialize response from server");
}
else if (!result.IsSuccess)
{
throw new InvalidOperationException($"Error from server: {result.Message}");
}
else if (result.Data == null || !result.Data.Any())
{
return [];
}
return [.. result.Data.Select(e => new ScriptMapElement(map, e, UpdateIsOpenAsync, UpdatePropertiesAsync))];
}
}
public Task<IElement> GetElement(string map, string name) => GetElement(map, name, true);
private async Task<IElement> GetElement(string map, string name, bool retry)
{
if (string.IsNullOrEmpty(map) || string.IsNullOrEmpty(name))
{
throw new ArgumentException("Map name and element name cannot be null or empty");
}
var accessToken = await RequestAccessToken();
using var client = new HttpClient()
{
BaseAddress = new Uri(MapManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Get, $"api/ScriptElements/{map}/element/{name}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
CachedToken = null; // Clear cached token to force re-authentication
return await GetElement(map, name, false); // Retry without recall
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate.");
}
}
else if (status.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to get element: {status.ReasonPhrase}");
}
else
{
var result = await response.Content.ReadFromJsonAsync<MessageResult<ElementDto>>();
if (result == null)
{
throw new InvalidOperationException("Failed to deserialize response from server");
}
else if (!result.IsSuccess)
{
throw new InvalidOperationException($"Error from server: {result.Message}");
}
else if (result.Data == null)
{
throw new KeyNotFoundException("Element not found in response");
}
else
{
return new ScriptMapElement(map, result.Data, UpdateIsOpenAsync, UpdatePropertiesAsync);
}
}
}
private Task UpdateIsOpenAsync(string mapName, string elementName, bool isOpen) => UpdateIsOpenAsync(mapName, elementName, isOpen, true);
private async Task UpdateIsOpenAsync(string mapName, string elementName, bool isOpen, bool retry)
{
var accessToken = await RequestAccessToken();
using var client = new HttpClient()
{
BaseAddress = new Uri(MapManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Patch, $"api/ScriptElements/{mapName}/element/{elementName}/IsOpen?isOpen={isOpen}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
//request.Content = JsonContent.Create(new { isOpen });
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
CachedToken = null; // Clear cached token to force re-authentication
await UpdateIsOpenAsync(mapName, elementName, isOpen, false); // Retry without recall
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate.");
}
}
else if (status.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to update IsOpen: {status.ReasonPhrase}");
}
else
{
var result = await response.Content.ReadFromJsonAsync<MessageResult>();
if (result == null)
{
throw new InvalidOperationException("Failed to deserialize response from server");
}
else if (!result.IsSuccess)
{
throw new InvalidOperationException($"Error from server: {result.Message}");
}
}
}
private Task UpdatePropertiesAsync(string mapName, string elementName, ElementPropertyUpdateModel model)
=> UpdatePropertiesAsync(mapName, elementName, model, true);
private async Task UpdatePropertiesAsync(string mapName, string elementName, ElementPropertyUpdateModel model, bool retry)
{
var accessToken = await RequestAccessToken();
using var client = new HttpClient()
{
BaseAddress = new Uri(MapManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Patch, $"api/ScriptElements/{mapName}/element/{elementName}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Đưa modelExpression vào body của request
request.Content = JsonContent.Create(model);
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
CachedToken = null; // Clear cached token to force re-authentication
await UpdatePropertiesAsync(mapName, elementName, model, false); // Retry without recall
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate.");
}
}
else if (status.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to get elements: {status.ReasonPhrase}");
}
else
{
var result = await response.Content.ReadFromJsonAsync<MessageResult>();
if (result == null)
{
throw new InvalidOperationException("Failed to deserialize response from server");
}
else if (!result.IsSuccess)
{
throw new InvalidOperationException($"Error from server: {result.Message}");
}
}
}
public async Task<string?> RequestAccessToken()
{
try
{
if (!string.IsNullOrEmpty(CachedToken) && DateTime.UtcNow < TokenExpiry)
{
return CachedToken;
}
var result = await openIddictClient.AuthenticateWithClientCredentialsAsync(new()
{
Scopes = [.. MapManagerScopes],
});
if (result == null || result.AccessToken == null || result.AccessTokenExpirationDate == null)
{
return null;
}
else
{
TokenExpiry = result.AccessTokenExpirationDate.Value.UtcDateTime;
CachedToken = result.AccessToken;
return CachedToken;
}
}
catch
{
return null;
}
}
public Task<INode> GetNode(string map, string name) => GetNode(map, name, true);
private async Task<INode> GetNode(string map, string name, bool retry)
{
if (string.IsNullOrEmpty(map) || string.IsNullOrEmpty(name))
{
throw new ArgumentException("Map name and element name cannot be null or empty");
}
var accessToken = await RequestAccessToken();
using var client = new HttpClient()
{
BaseAddress = new Uri(MapManagerUrl)
};
using var request = new HttpRequestMessage(HttpMethod.Get, $"api/ScriptElements/{map}/node/{name}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using var response = await client.SendAsync(request);
using var status = response.EnsureSuccessStatusCode();
if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retry)
{
CachedToken = null; // Clear cached token to force re-authentication
return await GetNode(map, name, false); // Retry without recall
}
else
{
throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate.");
}
}
else if (status.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new HttpRequestException($"Failed to get element: {status.ReasonPhrase}");
}
else
{
var result = await response.Content.ReadFromJsonAsync<MessageResult<NodeDto>>();
if (result == null)
{
throw new InvalidOperationException("Failed to deserialize response from server");
}
else if (!result.IsSuccess)
{
throw new InvalidOperationException($"Error from server: {result.Message}");
}
else if (result.Data == null)
{
throw new KeyNotFoundException("Element not found in response");
}
else
{
return new ScriptMapNode(result.Data.Id, result.Data.MapId, result.Data.Name, result.Data.X, result.Data.Y, result.Data.Theta);
}
}
}
}