diff options
| author | crupest <crupest@outlook.com> | 2022-12-09 18:22:20 +0800 | 
|---|---|---|
| committer | crupest <crupest@outlook.com> | 2022-12-20 20:32:53 +0800 | 
| commit | a586767b9b6e122891a8cddba57aecef11ef4bd2 (patch) | |
| tree | 058f34a17aa0cff56c702ace3f84d1918e541789 | |
| parent | bed807e3f8fab2f8b6ea3409886aac9f23f0f761 (diff) | |
| download | crupest-a586767b9b6e122891a8cddba57aecef11ef4bd2.tar.gz crupest-a586767b9b6e122891a8cddba57aecef11ef4bd2.tar.bz2 crupest-a586767b9b6e122891a8cddba57aecef11ef4bd2.zip | |
Develop secret api. v21
11 files changed, 259 insertions, 125 deletions
| diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs index e67b7c0..d278d23 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs @@ -8,17 +8,21 @@ public class ColumnHooks  {      public delegate void ColumnHookAction(ColumnInfo column, ref object? value); -    public ColumnHooks(ColumnHookAction afterGet, ColumnHookAction beforeSet) +    public ColumnHooks(ColumnHookAction afterSelect, ColumnHookAction beforeInsert, ColumnHookAction beforeUpdate)      { -        AfterGet = afterGet; -        BeforeSet = beforeSet; +        AfterSelect = afterSelect; +        BeforeInsert = beforeInsert; +        BeforeUpdate = beforeUpdate;      }      // Called after SELECT. -    public ColumnHookAction AfterGet; +    public ColumnHookAction AfterSelect; -    // Called before UPDATE and INSERT. -    public ColumnHookAction BeforeSet; +    // Called before INSERT. +    public ColumnHookAction BeforeInsert; + +    // Called before UPDATE +    public ColumnHookAction BeforeUpdate;  }  public class ColumnInfo @@ -32,8 +36,9 @@ public class ColumnInfo          ColumnType = typeProvider.Get(clrType);          Hooks = new ColumnHooks( -            new ColumnHooks.ColumnHookAction(OnAfterGet), -            new ColumnHooks.ColumnHookAction(OnBeforeSet) +            new ColumnHooks.ColumnHookAction(OnAfterSelect), +            new ColumnHooks.ColumnHookAction(OnBeforeInsert), +            new ColumnHooks.ColumnHookAction(OnBeforeUpdate)          );      } @@ -49,8 +54,9 @@ public class ColumnInfo          }          Hooks = new ColumnHooks( -            new ColumnHooks.ColumnHookAction(OnAfterGet), -            new ColumnHooks.ColumnHookAction(OnBeforeSet) +            new ColumnHooks.ColumnHookAction(OnAfterSelect), +            new ColumnHooks.ColumnHookAction(OnBeforeInsert), +            new ColumnHooks.ColumnHookAction(OnBeforeUpdate)          );      } @@ -72,12 +78,21 @@ public class ColumnInfo          }      } -    protected void OnAfterGet(ColumnInfo column, ref object? value) +    protected void OnAfterSelect(ColumnInfo column, ref object? value)      {          TryCoerceStringFromNullToEmpty(ref value);      } -    protected void OnBeforeSet(ColumnInfo column, ref object? value) +    protected void OnBeforeInsert(ColumnInfo column, ref object? value) +    { +        TryCoerceStringFromNullToEmpty(ref value); +        if (column.IsNotNull && !column.IsAutoIncrement) +        { +            throw new Exception($"Column {column.ColumnName} can't be empty."); +        } +    } + +    protected void OnBeforeUpdate(ColumnInfo column, ref object? value)      {          TryCoerceStringFromNullToEmpty(ref value);      } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs index 05ee269..91e49f8 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs @@ -7,7 +7,16 @@ public static class ColumnMetadataKeys      public const string IsPrimaryKey = nameof(ColumnAttribute.IsPrimaryKey);      public const string IsAutoIncrement = nameof(ColumnAttribute.IsAutoIncrement);      public const string Index = nameof(ColumnAttribute.Index); + +    /// <summary> +    /// This will add hooks for string type column to coerce null to ""(empty string) when get or set. No effect on non-string type. +    /// </summary>       public const string DefaultEmptyForString = nameof(ColumnAttribute.DefaultEmptyForString); + +    /// <summary> +    /// This indicates that you take care of generate this column value when create entity. User calling the api can not specify the value. +    /// </summary> +    public const string ClientGenerate = nameof(ColumnAttribute.DefaultEmptyForString);  }  public interface IColumnMetadata @@ -72,9 +81,12 @@ public class ColumnAttribute : Attribute, IColumnMetadata      // default None      public ColumnIndexType Index { get; init; } = ColumnIndexType.None; -    // Use empty string for default value of string type. +    /// <see cref="ColumnMetadataKeys.DefaultEmptyForString"/>      public bool DefaultEmptyForString { get; init; } +    /// <see cref="ColumnMetadataKeys.ClientGenerate"/> +    public bool ClientGenerate { get; init; } +      public bool TryGetValue(string key, out object? value)      {          var property = GetType().GetProperty(key); diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs index 70fdbfd..7591271 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs @@ -20,6 +20,7 @@ public interface IColumnTypeInfo      Type ClrType { get; }      Type DatabaseClrType { get; } +    bool IsSimple { get { return ClrType == DatabaseClrType; } }      DbType DbType      {          get @@ -84,14 +85,14 @@ public interface IColumnTypeInfo      // You must override this method if ClrType != DatabaseClrType      object? ConvertFromDatabase(object? databaseValue)      { -        Debug.Assert(ClrType == DatabaseClrType); +        Debug.Assert(IsSimple);          return databaseValue;      }      // You must override this method if ClrType != DatabaseClrType      object? ConvertToDatabase(object? value)      { -        Debug.Assert(ClrType == DatabaseClrType); +        Debug.Assert(IsSimple);          return value;      }  } @@ -178,6 +179,12 @@ public class ColumnTypeProvider : IColumnTypeProvider          }          else          { +            if (clrType == typeof(Nullable<>)) +            { +                clrType = clrType.GetGenericArguments()[0]; +                return Get(clrType); +            } +              throw new Exception($"Unsupported type: {clrType}");          }      } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs index 9402d69..e098aca 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs @@ -1,23 +1,21 @@  using System.Data;  using Dapper; -using Microsoft.Extensions.Options;  namespace CrupestApi.Commons.Crud; -// TODO: Implement and register this service. -public class CrudService<TEntity> : IDisposable +public class CrudService<TEntity> : IDisposable where TEntity : class  {      protected readonly TableInfo _table; +    protected readonly string? _connectionName;      protected readonly IDbConnection _dbConnection; -    protected readonly IOptionsSnapshot<CrupestApiConfig> _crupestApiOptions;      private readonly ILogger<CrudService<TEntity>> _logger; -    public CrudService(ITableInfoFactory tableInfoFactory, IDbConnectionFactory dbConnectionFactory, IOptionsSnapshot<CrupestApiConfig> crupestApiOptions, ILogger<CrudService<TEntity>> logger) +    public CrudService(string? connectionName, ITableInfoFactory tableInfoFactory, IDbConnectionFactory dbConnectionFactory, ILoggerFactory loggerFactory)      { +        _connectionName = connectionName;          _table = tableInfoFactory.Get(typeof(TEntity)); -        _dbConnection = dbConnectionFactory.Get(); -        _crupestApiOptions = crupestApiOptions; -        _logger = logger; +        _dbConnection = dbConnectionFactory.Get(_connectionName); +        _logger = loggerFactory.CreateLogger<CrudService<TEntity>>();          if (!_table.CheckExistence(_dbConnection))          { @@ -36,4 +34,29 @@ public class CrudService<TEntity> : IDisposable      {          _dbConnection.Dispose();      } + +    public List<TEntity> Select(IWhereClause? filter) +    { +        return _table.Select(_dbConnection, filter).Cast<TEntity>().ToList(); +    } + +    public int Insert(IInsertClause insertClause) +    { +        return _table.Insert(_dbConnection, insertClause); +    } + +    public int Insert(TEntity entity) +    { +        return _table.Insert(_dbConnection, _table.GenerateInsertClauseFromEntity(entity)); +    } + +    public int Update(IUpdateClause updateClause, IWhereClause? filter) +    { +        return _table.Update(_dbConnection, filter, updateClause); +    } + +    public int Delete(IWhereClause? filter) +    { +        return _table.Delete(_dbConnection, filter); +    }  } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs index 92b5660..29504f4 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs @@ -6,7 +6,9 @@ public static class CrudServiceCollectionExtensions  {      public static IServiceCollection UseCrud(this IServiceCollection services)      { +        services.TryAddSingleton<IDbConnectionFactory, SqliteConnectionFactory>();          services.TryAddSingleton<IColumnTypeProvider, ColumnTypeProvider>(); +        services.TryAddSingleton<ITableInfoFactory, TableInfoFactory>();          return services;      }  } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs index 80d1b22..2ee01ca 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs @@ -1,4 +1,6 @@  using System.Data; +using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Options;  namespace CrupestApi.Commons.Crud; @@ -7,3 +9,24 @@ public interface IDbConnectionFactory  {      IDbConnection Get(string? name = null);  } + +public class SqliteConnectionFactory : IDbConnectionFactory +{ +    private readonly IOptionsMonitor<CrupestApiConfig> _apiConfigMonitor; + +    public SqliteConnectionFactory(IOptionsMonitor<CrupestApiConfig> apiConfigMonitor) +    { +        _apiConfigMonitor = apiConfigMonitor; +    } + +    public IDbConnection Get(string? name = null) +    { +        var connectionString = new SqliteConnectionStringBuilder() +        { +            DataSource = Path.Combine(_apiConfigMonitor.CurrentValue.DataDir, $"{name ?? "crupest-api"}.db"), +            Mode = SqliteOpenMode.ReadWriteCreate +        }.ToString(); + +        return new SqliteConnection(connectionString); +    } +} diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs index 73082c0..4836720 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs @@ -189,12 +189,9 @@ CREATE TABLE {tableName}(      public void CheckColumnName(string columnName)      { -        foreach (var c in columnName) +        if (!ColumnNameList.Contains(columnName))          { -            if (char.IsWhiteSpace(c)) -            { -                throw new Exception("White space found in column name, which might be an sql injection attack!"); -            } +            throw new ArgumentException($"Column {columnName} is not in the table.");          }      } @@ -206,10 +203,6 @@ CREATE TABLE {tableName}(              foreach (var column in relatedColumns)              {                  CheckColumnName(column); -                if (!ColumnNameList.Contains(column)) -                { -                    throw new ArgumentException($"Column {column} is not in the table."); -                }              }          }      } @@ -258,13 +251,15 @@ CREATE TABLE {tableName}(          return (result.ToString(), parameters);      } -    public InsertClause GenerateInsertClauseFromObject(object value) +    public InsertClause GenerateInsertClauseFromEntity(object entity)      { +        Debug.Assert(EntityType.IsInstanceOfType(entity)); +          var insertClause = InsertClause.Create();          foreach (var column in ColumnInfos)          { -            var propertyInfo = EntityType.GetProperty(column.ColumnName); +            var propertyInfo = column.PropertyInfo;              if (propertyInfo is null)              {                  if (column.IsAutoIncrement) @@ -277,7 +272,7 @@ CREATE TABLE {tableName}(                  }              } -            var propertyValue = propertyInfo.GetValue(value); +            var propertyValue = propertyInfo.GetValue(entity);              if (propertyValue is null)              {                  if (column.IsAutoIncrement) @@ -360,52 +355,63 @@ CREATE TABLE {tableName}(          return (sb.ToString(), parameters);      } -    private object? ClearNonColumnProperties(object? entity) +    private DynamicParameters ConvertParameters(DynamicParameters parameters)      { -        Debug.Assert(entity is null || entity.GetType() == EntityType); -        if (entity is null) return entity; -        foreach (var property in NonColumnProperties) +        var result = new DynamicParameters(); +        foreach (var paramName in parameters.ParameterNames)          { -            // Clear any non-column properties. -            property.SetValue(entity, Activator.CreateInstance(property.PropertyType)); +            var value = parameters.Get<object?>(paramName); +            if (value is null) +            { +                result.Add(paramName, null); +                continue; +            } +            var typeInfo = _columnTypeProvider.Get(value.GetType()); +            result.Add(paramName, typeInfo.ConvertToDatabase(value));          } -        return entity; +        return result;      } -    private object? CallColumnHook(object? entity, string hookName) +    private object? ConvertFromDynamicToEntity(dynamic d)      { -        Debug.Assert(entity is null || entity.GetType() == EntityType); -        if (entity is null) return entity; +        if (d is null) return null; + +        var result = Activator.CreateInstance(EntityType); +          foreach (var column in ColumnInfos)          { -            var property = column.PropertyInfo; -            if (property is not null) +            var propertyInfo = column.PropertyInfo; +            if (propertyInfo is not null)              { -                var value = property.GetValue(entity); - -                switch (hookName) -                { -                    case "AfterGet": -                        column.Hooks.AfterGet(column, ref value); -                        break; -                    case "BeforeSet": -                        column.Hooks.BeforeSet(column, ref value); -                        break; -                    default: -                        throw new Exception("Unknown hook."); -                }; - -                property.SetValue(entity, value); +                object? value = d[column.ColumnName]; +                value = column.ColumnType.ConvertFromDatabase(value); +                propertyInfo.SetValue(result, value);              } -          } -        return entity; + +        return result;      }      public virtual IEnumerable<object?> Select(IDbConnection dbConnection, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null)      {          var (sql, parameters) = GenerateSelectSql(where, orderBy, skip, limit); -        return dbConnection.Query(EntityType, sql, parameters).Select(e => CallColumnHook(ClearNonColumnProperties(e), "AfterGet")); +        return dbConnection.Query<dynamic>(sql, parameters).Select(d => +        { +            var e = ConvertFromDynamicToEntity(d); + +            foreach (var column in ColumnInfos) +            { +                var propertyInfo = column.PropertyInfo; +                if (propertyInfo is not null) +                { +                    var value = propertyInfo.GetValue(e); +                    column.Hooks.AfterSelect(column, ref value); +                    propertyInfo.SetValue(e, value); +                } +            } + +            return e; +        });      }      public virtual int Insert(IDbConnection dbConnection, IInsertClause insert) @@ -416,11 +422,11 @@ CREATE TABLE {tableName}(          {              var column = GetColumn(item.ColumnName);              var value = item.Value; -            column.Hooks.BeforeSet?.Invoke(column, ref value); +            column.Hooks.BeforeInsert(column, ref value);              item.Value = value;          } -        return dbConnection.Execute(sql, parameters); +        return dbConnection.Execute(sql, ConvertParameters(parameters));      }      public virtual int Update(IDbConnection dbConnection, IWhereClause? where, IUpdateClause update) @@ -431,21 +437,45 @@ CREATE TABLE {tableName}(          {              var column = GetColumn(item.ColumnName);              var value = item.Value; -            column.Hooks.BeforeSet?.Invoke(column, ref value); +            column.Hooks.BeforeUpdate(column, ref value);              item.Value = value;          } -        return dbConnection.Execute(sql, parameters); +        return dbConnection.Execute(sql, ConvertParameters(parameters));      }      public virtual int Delete(IDbConnection dbConnection, IWhereClause? where)      {          var (sql, parameters) = GenerateDeleteSql(where); -        return dbConnection.Execute(sql, parameters); +        return dbConnection.Execute(sql, ConvertParameters(parameters));      }  } -// TODO: Implement and register this service.  public interface ITableInfoFactory  {      TableInfo Get(Type type);  } + +public class TableInfoFactory : ITableInfoFactory +{ +    private readonly Dictionary<Type, TableInfo> _cache = new Dictionary<Type, TableInfo>(); +    private readonly IColumnTypeProvider _columnTypeProvider; + +    public TableInfoFactory(IColumnTypeProvider columnTypeProvider) +    { +        _columnTypeProvider = columnTypeProvider; +    } + +    public TableInfo Get(Type type) +    { +        if (_cache.TryGetValue(type, out var tableInfo)) +        { +            return tableInfo; +        } +        else +        { +            tableInfo = new TableInfo(type, _columnTypeProvider); +            _cache.Add(type, tableInfo); +            return tableInfo; +        } +    } +} diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs index 8a0b7ac..8ae2c01 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs @@ -15,12 +15,12 @@ public class CompositeWhereClause : IWhereClause      {          ConcatOp = concatOp;          ParenthesesSubclause = parenthesesSubclause; -        Subclauses = subclauses; +        Subclauses = subclauses.ToList();      }      public string ConcatOp { get; }      public bool ParenthesesSubclause { get; } -    public IWhereClause[] Subclauses { get; } +    public List<IWhereClause> Subclauses { get; }      public (string sql, DynamicParameters parameters) GenerateSql(string? dbProviderId = null)      { @@ -106,7 +106,7 @@ public class SimpleCompareWhereClause : IWhereClause  {      public string Column { get; }      public string Operator { get; } -    public object Value { get; } +    public object? Value { get; }      public List<string> GetRelatedColumns()      { @@ -114,44 +114,44 @@ public class SimpleCompareWhereClause : IWhereClause      }      // It's user's responsibility to keep column safe, with proper escape. -    public SimpleCompareWhereClause(string column, string op, object value) +    public SimpleCompareWhereClause(string column, string op, object? value)      {          Column = column;          Operator = op;          Value = value;      } -    public static SimpleCompareWhereClause Create(string column, string op, object value) +    public static SimpleCompareWhereClause Create(string column, string op, object? value)      {          return new SimpleCompareWhereClause(column, op, value);      } -    public static SimpleCompareWhereClause Eq(string column, object value) +    public static SimpleCompareWhereClause Eq(string column, object? value)      {          return new SimpleCompareWhereClause(column, "=", value);      } -    public static SimpleCompareWhereClause Neq(string column, object value) +    public static SimpleCompareWhereClause Neq(string column, object? value)      {          return new SimpleCompareWhereClause(column, "<>", value);      } -    public static SimpleCompareWhereClause Gt(string column, object value) +    public static SimpleCompareWhereClause Gt(string column, object? value)      {          return new SimpleCompareWhereClause(column, ">", value);      } -    public static SimpleCompareWhereClause Gte(string column, object value) +    public static SimpleCompareWhereClause Gte(string column, object? value)      {          return new SimpleCompareWhereClause(column, ">=", value);      } -    public static SimpleCompareWhereClause Lt(string column, object value) +    public static SimpleCompareWhereClause Lt(string column, object? value)      {          return new SimpleCompareWhereClause(column, "<", value);      } -    public static SimpleCompareWhereClause Lte(string column, object value) +    public static SimpleCompareWhereClause Lte(string column, object? value)      {          return new SimpleCompareWhereClause(column, "<=", value);      } @@ -163,3 +163,20 @@ public class SimpleCompareWhereClause : IWhereClause          return ($"{Column} {Operator} @{parameterName}", parameters);      }  } + +public class WhereClause : AndWhereClause +{ +    public WhereClause() +    { +    } + +    public void Add(IWhereClause subclause) +    { +        Subclauses.Add(subclause); +    } + +    public void Eq(string column, object? value) +    { +        Subclauses.Add(SimpleCompareWhereClause.Eq(column, value)); +    } +} diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs index 5172b34..3017176 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs @@ -1,11 +1,39 @@  using System.Text.Json; -using CrupestApi.Commons.Crud;  using Microsoft.Extensions.Options;  namespace CrupestApi.Commons;  public static class CrupestApiJsonExtensions  { +    public static object? CheckJsonValueNotArrayOrObject(this JsonElement value) +    { +        if (value.ValueKind == JsonValueKind.Null && value.ValueKind == JsonValueKind.Undefined) +        { +            return null; +        } +        else if (value.ValueKind == JsonValueKind.True) +        { +            return true; +        } +        else if (value.ValueKind == JsonValueKind.False) +        { +            return false; +        } +        else if (value.ValueKind == JsonValueKind.Number) +        { +            return value.GetDouble(); +        } +        else if (value.ValueKind == JsonValueKind.String) +        { +            return value.GetString(); +        } +        else +        { +            throw new Exception("Only value not array or object is allowed.") +        } +    } + +      public static IServiceCollection AddJsonOptions(this IServiceCollection services)      {          services.AddOptions<JsonSerializerOptions>(); diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretInfo.cs index d4c1b66..009bde9 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretInfo.cs @@ -6,12 +6,14 @@ public class SecretInfo  {      [Column(NotNull = true)]      public string Key { get; set; } = default!; -    [Column(NotNull = true)] +    [Column(NotNull = true, ClientGenerate = true)]      public string Secret { get; set; } = default!;      [Column(DefaultEmptyForString = true)]      public string Description { get; set; } = default!;      [Column(NotNull = false)] -    public DateTime ExpireTime { get; set; } +    public DateTime? ExpireTime { get; set; } +    [Column(NotNull = true)]      public bool Revoked { get; set; } +    [Column(NotNull = true)]      public DateTime CreateTime { get; set; }  } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs b/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs index 23a0e82..5a49121 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsService.cs @@ -1,66 +1,31 @@ -using System.Data;  using System.Diagnostics; -using System.Security.Cryptography; -using System.Text;  using CrupestApi.Commons;  using CrupestApi.Commons.Crud;  using Dapper; -using Microsoft.Data.Sqlite; -using Microsoft.Extensions.Options;  namespace CrupestApi.Secrets;  public class SecretsService : CrudService<SecretInfo>, ISecretsService  { -    private readonly IOptionsSnapshot<CrupestApiConfig> _crupestApiConfig;      private readonly ILogger<SecretsService> _logger; -    public SecretsService(IOptionsSnapshot<CrupestApiConfig> crupestApiConfig, ILogger<SecretsService> logger, ServiceProvider services) -    : base(services) +    public SecretsService(ITableInfoFactory tableInfoFactory, IDbConnectionFactory dbConnectionFactory, ILoggerFactory loggerFactory) +    : base("secrets", tableInfoFactory, dbConnectionFactory, loggerFactory)      { -        _crupestApiConfig = crupestApiConfig; -        _logger = logger; -    } - -    private string GetDatabasePath() -    { -        return Path.Combine(_crupestApiConfig.Value.DataDir, "secrets.db"); -    } - -    public override string GetDbConnectionString() -    { -        var fileName = GetDatabasePath(); - -        return new SqliteConnectionStringBuilder() -        { -            DataSource = fileName, -            Mode = SqliteOpenMode.ReadWriteCreate -        }.ToString(); -    } - -    private string GenerateRandomKey(int length) -    { -        const string alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -        using var randomNumberGenerator = RandomNumberGenerator.Create(); -        var result = new StringBuilder(length); -        for (int i = 0; i < length; i++) -        { -            result.Append(alphanum[i]); -        } -        return result.ToString(); +        _logger = loggerFactory.CreateLogger<SecretsService>();      }      public async Task<SecretInfo> CreateSecretAsync(SecretInfo secretInfo)      {          if (secretInfo.Secret is not null)          { -            throw new ArgumentException("Secret is auto generated. Don't specify it explicit.") +            throw new ArgumentException("Secret is auto generated. Don't specify it explicit.");          }          secretInfo.Secret = GenerateRandomKey(16);          secretInfo.CreateTime = DateTime.Now; -        await InsertAsync(_table.GenerateInsertClauseFromObject(secretInfo)); +        await InsertAsync(_table.GenerateInsertClauseFromEntity(secretInfo));          return secretInfo;      } @@ -233,4 +198,14 @@ SELECT Id, Key, Secret, Description, ExpireTime, Revoked, CreateTime FROM secret          await VerifySecretAsync(key, secret);      } + +    public Task<SecretInfo?> GetSecretAsync(string secret) +    { +        throw new NotImplementedException(); +    } + +    public Task<SecretInfo> CreateSecretAsync(string key, string description, DateTime? expireTime = null) +    { +        throw new NotImplementedException(); +    }  } | 
