From 93a277c1368899d5c0116efe4f470f00f43dec2a Mon Sep 17 00:00:00 2001 From: Jiwon Date: Wed, 29 Nov 2023 16:06:19 +0900 Subject: [PATCH] [Feature] Add Restapi acs (#2322) --- .../AccessControlServiceController.cs | 4 +- .../Services/AccessControlServiceFactory.cs | 8 +++- .../Services/RedisAccessControlService.cs | 2 - .../Services/RestAPIAccessControlService.cs | 44 +++++++++++++++++ .../Services/SQLiteAccessControlService.cs | 48 ++++++++++++++----- 5 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 NineChronicles.Headless/Services/RestAPIAccessControlService.cs diff --git a/NineChronicles.Headless.AccessControlCenter/Controllers/AccessControlServiceController.cs b/NineChronicles.Headless.AccessControlCenter/Controllers/AccessControlServiceController.cs index 5b2f0f303..391949ae8 100644 --- a/NineChronicles.Headless.AccessControlCenter/Controllers/AccessControlServiceController.cs +++ b/NineChronicles.Headless.AccessControlCenter/Controllers/AccessControlServiceController.cs @@ -27,7 +27,9 @@ public AccessControlServiceController(IMutableAccessControlService accessControl [HttpGet("entries/{address}")] public ActionResult GetTxQuota(string address) { - return _accessControlService.GetTxQuota(new Address(address)); + var result = _accessControlService.GetTxQuota(new Address(address)); + + return result != null ? result : NotFound(); } [HttpPost("entries/add-tx-quota/{address}")] diff --git a/NineChronicles.Headless/Services/AccessControlServiceFactory.cs b/NineChronicles.Headless/Services/AccessControlServiceFactory.cs index 0ff8e476a..4284c1413 100644 --- a/NineChronicles.Headless/Services/AccessControlServiceFactory.cs +++ b/NineChronicles.Headless/Services/AccessControlServiceFactory.cs @@ -15,7 +15,12 @@ public enum StorageType /// /// Use SQLite /// - SQLite + SQLite, + + /// + /// Use RestAPI + /// + RestAPI } public static IAccessControlService Create( @@ -27,6 +32,7 @@ string connectionString { StorageType.Redis => new RedisAccessControlService(connectionString), StorageType.SQLite => new SQLiteAccessControlService(connectionString), + StorageType.RestAPI => new RestAPIAccessControlService(connectionString), _ => throw new ArgumentOutOfRangeException(nameof(storageType), storageType, null) }; } diff --git a/NineChronicles.Headless/Services/RedisAccessControlService.cs b/NineChronicles.Headless/Services/RedisAccessControlService.cs index de19e8c9c..b6047fe40 100644 --- a/NineChronicles.Headless/Services/RedisAccessControlService.cs +++ b/NineChronicles.Headless/Services/RedisAccessControlService.cs @@ -28,8 +28,6 @@ public RedisAccessControlService(string storageUri) RedisValue result = _db.StringGet(address.ToString()); if (!result.IsNull) { - Log.ForContext("Source", nameof(IAccessControlService)) - .Verbose("\"{Address}\" Tx Quota: {Quota}", address, result); return Convert.ToInt32(result); } diff --git a/NineChronicles.Headless/Services/RestAPIAccessControlService.cs b/NineChronicles.Headless/Services/RestAPIAccessControlService.cs new file mode 100644 index 000000000..ad9c25ff3 --- /dev/null +++ b/NineChronicles.Headless/Services/RestAPIAccessControlService.cs @@ -0,0 +1,44 @@ +using System; +using System.Net.Http; +using Libplanet.Crypto; +using Nekoyume.Blockchain; +using Serilog; + +namespace NineChronicles.Headless.Services +{ + public class RestAPIAccessControlService : IAccessControlService + { + private readonly HttpClient _httpClient; + private readonly string _baseUrl; + + public RestAPIAccessControlService(string baseUrl) + { + _baseUrl = baseUrl; + _httpClient = new HttpClient + { + Timeout = TimeSpan.FromMilliseconds(500) + }; + } + + public int? GetTxQuota(Address address) + { + try + { + string requestUri = $"{_baseUrl}/entries/{address}"; + HttpResponseMessage response = _httpClient.GetAsync(requestUri).GetAwaiter().GetResult(); + + if (response.IsSuccessStatusCode) + { + string resultString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + return Convert.ToInt32(resultString); + } + } + catch (Exception ex) + { + Log.Error(ex, "Error occurred while calling Rest API."); + } + + return null; + } + } +} diff --git a/NineChronicles.Headless/Services/SQLiteAccessControlService.cs b/NineChronicles.Headless/Services/SQLiteAccessControlService.cs index f3e7263c8..1ddfaac65 100644 --- a/NineChronicles.Headless/Services/SQLiteAccessControlService.cs +++ b/NineChronicles.Headless/Services/SQLiteAccessControlService.cs @@ -10,6 +10,7 @@ public class SQLiteAccessControlService : IAccessControlService { private const string CreateTableSql = "CREATE TABLE IF NOT EXISTS txquotalist (address VARCHAR(42), quota INT)"; + private const string CreateIndexSql = "CREATE INDEX IF NOT EXISTS idx_address ON txquotalist (address);"; private const string GetTxQuotaSql = "SELECT quota FROM txquotalist WHERE address=@Address"; @@ -18,31 +19,52 @@ public class SQLiteAccessControlService : IAccessControlService public SQLiteAccessControlService(string connectionString) { _connectionString = connectionString; - using var connection = new SqliteConnection(_connectionString); - connection.Open(); + try + { + using var connection = new SqliteConnection(_connectionString); + connection.Open(); - using var command = connection.CreateCommand(); - command.CommandText = CreateTableSql; - command.ExecuteNonQuery(); + ExecuteNonQuery(connection, CreateTableSql); + ExecuteNonQuery(connection, CreateIndexSql); + } + catch (Exception ex) + { + Log.Error(ex, "An error occurred while initializing the database."); + throw; + } } public int? GetTxQuota(Address address) { - using var connection = new SqliteConnection(_connectionString); - connection.Open(); + try + { + using var connection = new SqliteConnection(_connectionString); + connection.Open(); - using var command = connection.CreateCommand(); - command.CommandText = GetTxQuotaSql; - command.Parameters.AddWithValue("@Address", address.ToString()); + using var command = connection.CreateCommand(); + command.CommandText = GetTxQuotaSql; + command.Parameters.AddWithValue("@Address", address.ToString()); - var queryResult = command.ExecuteScalar(); + var queryResult = command.ExecuteScalar(); - if (queryResult != null) + if (queryResult != null && queryResult != DBNull.Value) + { + return Convert.ToInt32(queryResult); + } + } + catch (Exception ex) { - return Convert.ToInt32(queryResult); + Log.Error(ex, "An error occurred while getting transaction quota."); } return null; } + + private void ExecuteNonQuery(SqliteConnection connection, string commandText) + { + using var command = connection.CreateCommand(); + command.CommandText = commandText; + command.ExecuteNonQuery(); + } } }