CRUD implemented

This commit is contained in:
2025-09-22 15:35:31 +02:00
parent 9c006b0674
commit e2c588794e
11 changed files with 207 additions and 105 deletions

View File

@@ -19,6 +19,7 @@
<ItemGroup>
<ProjectReference Include="../Logic/Logic.csproj" />
<ProjectReference Include="../Repository/Repository.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Logic;
using Repository;
namespace Keychain;
@@ -25,8 +26,8 @@ class Program
private static ServiceProvider SetupServices()
{
var services = new ServiceCollection();
//services.AddTransient<IPasswordStoreService, PasswordStoreService>();
services.AddSingleton<IPasswordStoreService, PasswordStoreService>();
services.AddSingleton<IRepository, JsonRepository>();
return services.BuildServiceProvider();
}
}

View File

@@ -1,5 +1,5 @@
using Adw;
using Keychain.UI.ViewModels;
using Keychain.ViewModels;
namespace Keychain.UI;

View File

@@ -5,16 +5,9 @@ namespace Keychain.ViewModels;
public class PasswordStoreShortcut : INotifyPropertyChanged
{
private IPasswordService passwordService;
private IPasswordStoreService passwordService;
public event PropertyChangedEventHandler? PropertyChanged;
private string displayName;
private bool displayNameSet = false;
private string? iconName;
private string path;
public bool DisplayNameSet { get => displayNameSet; }
public string DisplayName
{
get => displayName;

View File

@@ -6,9 +6,8 @@ public interface IPasswordStoreService
{
IEnumerable<PasswordStore> GetAll();
PasswordStore Get(uint ID);
int Delete(uint ID);
int Delete(PasswordStore item);
int Create(string path, string? displayName = null, string? iconName = null);
int Create(PasswordStore item);
int Edit(uint ID, PasswordStore newItem);
void Delete(uint ID);
void Delete(PasswordStore item);
void Create(PasswordStore item);
void Edit(uint ID, PasswordStore newItem);
}

View File

@@ -7,38 +7,33 @@ public class PasswordStoreService : IPasswordStoreService
{
private readonly IRepository repository;
public int Create(string path, string? displayName = null, string? iconName = null)
public void Create(PasswordStore item)
{
Create(item);
}
public void Delete(uint ID)
{
throw new NotImplementedException();
}
public int Create(PasswordStore item)
public void Delete(PasswordStore item)
{
return Create(item.Path, item.DisplayName, item.IconName);
Delete(item.ID);
}
public int Delete(uint ID)
{
throw new NotImplementedException();
}
public int Delete(PasswordStore item)
{
return Delete(item.ID);
}
public int Edit(uint ID, PasswordStore newItem)
public void Edit(uint ID, PasswordStore newItem)
{
throw new NotImplementedException();
}
public PasswordStore Get(uint ID)
{
return repository.ReadAll().Where(item => item.ID.Equals(ID)).First();
return repository.Get(ID);
}
public IEnumerable<PasswordStore> GetAll()
{
return (IEnumerable<PasswordStore>)repository.ReadAll();
return (IEnumerable<PasswordStore>)repository.GetAll();
}
}

View File

@@ -2,8 +2,8 @@
public class PasswordStore
{
public uint ID;
public string Path;
public string? DisplayName;
public string? IconName;
public uint ID { get; set; }
public string Path { get; set; }
public string? DisplayName { get; set; }
public string? IconName { get; set; }
}

View File

@@ -1,10 +1,14 @@
using System.Collections;
using Models;
namespace Repository;
public interface IRepository
{
IEnumerable ReadAll();
void WriteAll(IEnumerable items);
object Get(uint id);
List<PasswordStore> GetAll();
PasswordStore? Get(uint id);
void Edit(uint ID, PasswordStore newItem);
void Create(PasswordStore item);
void Delete(uint ID);
void Delete(PasswordStore item);
}

View File

@@ -0,0 +1,172 @@
using System.Text.Json;
using Models;
namespace Repository;
public class JsonRepository : IRepository, IDisposable
{
private const string _appName = "Keychain";
private uint _autoIncrementedId;
private readonly string _filePath;
private List<PasswordStore> _cache;
private bool _cacheAhead;
public JsonRepository(string fileName)
{
string? xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
string dataHome;
if (!string.IsNullOrEmpty(xdgDataHome))
{
dataHome = Path.Combine(xdgDataHome, _appName);
}
else
{
dataHome = Path.Combine(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"),
_appName
);
}
_filePath = Path.Combine(dataHome, fileName);
ReadAllFromFile();
var lastItem = _cache.OrderBy(item => item.ID).LastOrDefault();
_autoIncrementedId = lastItem != null ? lastItem.ID : 0;
}
private void ReadAllFromFile()
{
var items = new List<PasswordStore>();
if (File.Exists(_filePath))
{
try
{
string json = File.ReadAllText(_filePath);
items = JsonSerializer.Deserialize<List<PasswordStore>>(json) ?? new List<PasswordStore>();
}
catch (JsonException e)
{
WriteToStdErr($"JSON error: {e.Message}");
}
catch (IOException e)
{
WriteToStdErr($"File I/O error: {e.Message}");
}
catch (Exception e)
{
WriteToStdErr($"Unexpected error: {e.Message}");
}
}
_cache = items;
_cacheAhead = false;
}
public List<PasswordStore> GetAll()
{
return _cache;
}
public PasswordStore? Get(uint id)
{
try
{
return _cache.First(item => item.ID.Equals(id));
}
catch (InvalidOperationException)
{
// Not found
return null;
}
catch (Exception e)
{
WriteToStdErr($"Unexpected error: {e.Message}");
return null;
}
}
public void Dispose()
{
if (_cacheAhead)
{
try
{
string json = JsonSerializer.Serialize(_cache);
string? directory = Path.GetDirectoryName(_filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory!);
}
File.WriteAllText(_filePath, json);
}
catch (IOException e)
{
WriteToStdErr($"File I/O error: {e.Message}");
}
catch (Exception e)
{
WriteToStdErr($"Unexpected error: {e.Message}");
}
ReadAllFromFile();
}
}
public void Edit(uint ID, PasswordStore newItem)
{
try
{
PasswordStore item = _cache.First(item => item.ID.Equals(ID));
item.DisplayName = newItem.DisplayName;
item.IconName = newItem.IconName;
item.Path = newItem.Path;
_cacheAhead = true;
}
catch (InvalidOperationException)
{
WriteToStdErr($"Edit error: Item with ID {ID} not found.");
}
catch (Exception e)
{
WriteToStdErr($"Unexpected error: {e.Message}");
}
}
public void Create(PasswordStore item)
{
item.ID = ++_autoIncrementedId;
_cache.Add(item);
_cacheAhead = true;
}
public void Delete(PasswordStore item)
{
Delete(item.ID);
}
public void Delete(uint id)
{
try
{
var item = _cache.First(item => item.ID.Equals(id));
_cache.Remove(item);
_cacheAhead = true;
}
catch (InvalidOperationException)
{
WriteToStdErr($"Delete error: Item with ID {id} not found.");
}
catch (Exception e)
{
WriteToStdErr($"Unexpected error: {e.Message}");
}
}
private void WriteToStdErr(string message)
{
using var sw = new StreamWriter(Console.OpenStandardError());
sw.WriteLine(message);
}
}

View File

@@ -1,67 +0,0 @@
using System.Collections;
using System.Text.Json;
namespace Repository;
public class Repository : IRepository
{
private const string _appName = "Keychain";
private readonly string _filePath;
private List<object>? _cache;
private bool _cacheDirty = true;
public Repository(string fileName)
{
var xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
string dataHome;
if (!string.IsNullOrEmpty(xdgDataHome))
{
dataHome = Path.Combine(xdgDataHome, _appName);
}
else
{
dataHome = Path.Combine(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"),
_appName
);
}
_filePath = Path.Combine(dataHome, fileName);
}
public IEnumerable ReadAll()
{
if (!_cacheDirty && _cache != null)
return _cache;
if (!File.Exists(_filePath))
{
_cache = new List<object>();
}
else
{
var json = File.ReadAllText(_filePath);
_cache = JsonSerializer.Deserialize<List<object>>(json) ?? new List<object>();
}
_cacheDirty = false;
return _cache;
}
public object Get(uint id)
{
}
public void WriteAll(IEnumerable items)
{
var json = JsonSerializer.Serialize(items);
var directory = Path.GetDirectoryName(_filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory!);
}
File.WriteAllText(_filePath, json);
_cache = (List<object>)items;
_cacheDirty = false;
}
}

View File

@@ -6,4 +6,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Models/Models.csproj" />
</ItemGroup>
</Project>