320 lines
12 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|