Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

112 major revised refactor #114

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/FluentCMS.Entities/AuditEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace FluentCMS.Entities;

public interface IAuditEntity : IEntity
{
// If system creates the entity, property should be null
string? CreatedBy { get; set; } // UserName
DateTime? CreatedAt { get; set; }

// If system updates the entity, property should be null
string? LastUpdatedBy { get; set; } // UserName
DateTime? LastUpdatedAt { get; set; }
}

public class AuditEntity : Entity, IAuditEntity
{

public string? CreatedBy { get; set; }
public DateTime? CreatedAt { get; set; }

public string? LastUpdatedBy { get; set; }
public DateTime? LastUpdatedAt { get; set; }
}
22 changes: 22 additions & 0 deletions src/FluentCMS.Entities/ContentType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace FluentCMS.Entities;

public class ContentType : AuditEntity
{
public required string Title { get; set; }
public required string Slug { get; set; }
}

public class ContentTypeField
{
public required string Title { get; set; }
public string? Description { get; set; }
public string? Label { get; set; }
public required bool Hidden { get; set; }
public string? DefaultValue { get; set; }
public required FieldType FieldType { get; set; }
}

public enum FieldType
{
Text, Number, Date, DateTime, Boolean
}
12 changes: 12 additions & 0 deletions src/FluentCMS.Entities/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace FluentCMS.Entities;

public interface IEntity
{
public Guid Id { get; set; }
}

public class Entity : IEntity
{
public Guid Id { get; set; }
}

9 changes: 9 additions & 0 deletions src/FluentCMS.Entities/FluentCMS.Entities.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
10 changes: 10 additions & 0 deletions src/FluentCMS.Entities/Page.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FluentCMS.Entities;

public class Page : AuditEntity
{
public Guid SiteId { get; set; }
public required string Title { get; set; }
public Guid? ParentId { get; set; }
public int Order { get; set; }
public required string Path { get; set; }
}
10 changes: 10 additions & 0 deletions src/FluentCMS.Entities/Role.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FluentCMS.Entities;

public class Role : AuditEntity
{
public required string Name { get; set; }
public string? Description { get; set; }
public required bool AutoAssigned { get; set; }

public Guid SiteId { get; set; }
}
9 changes: 9 additions & 0 deletions src/FluentCMS.Entities/Site.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FluentCMS.Entities;

public class Site : AuditEntity
{
public required string Name { get; set; }
public string? Description { get; set; }
public required List<string> Urls { get; set; } = [];
//public required Guid RoleId { get; set; }
}
9 changes: 9 additions & 0 deletions src/FluentCMS.Entities/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FluentCMS.Entities;

public class User : AuditEntity
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public required string Username { get; set; }
public required string Password { get; set; }
}
8 changes: 8 additions & 0 deletions src/FluentCMS.Entities/UserRole.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace FluentCMS.Entities;

public class UserRole : AuditEntity
{
public required Guid UserId { get; set; }
public required Guid RoleId { get; set; }
public required Guid SiteId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FluentCMS.Entities;

namespace FluentCMS.Repositories.Abstractions;

public interface IContentTypeRepository : IGenericRepository<ContentType>
{
Task<ContentType?> GetBySlug(string slug, CancellationToken cancellationToken = default);
}
20 changes: 20 additions & 0 deletions src/FluentCMS.Repositories/Abstractions/IGenericRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using FluentCMS.Entities;

namespace FluentCMS.Repositories.Abstractions;

public interface IGenericRepository<TEntity>
where TEntity : IEntity
{
Task<TEntity?> Create(TEntity entity, CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> CreateMany(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default);
Task<TEntity?> Update(TEntity entity, CancellationToken cancellationToken = default);
Task<TEntity?> Delete(Guid id, CancellationToken cancellationToken = default);

Task<IEnumerable<TEntity>> GetAll(CancellationToken cancellationToken = default);
Task<TEntity?> GetById(Guid id, CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> GetByIds(IEnumerable<Guid> ids, CancellationToken cancellationToken = default);

// TODO: do we need this to be exposed to other layers?
// in this case, we won't be able to optimize the query
//Task<IEnumerable<TEntity>> GetAll(Expression<Func<TEntity, bool>> filter, CancellationToken cancellationToken = default);
}
8 changes: 8 additions & 0 deletions src/FluentCMS.Repositories/Abstractions/IPageRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FluentCMS.Entities;

namespace FluentCMS.Repositories.Abstractions;

public interface IPageRepository : IGenericRepository<Page>
{
Task<IEnumerable<Page>> GetBySiteId(Guid siteId, CancellationToken cancellationToken = default);
}
9 changes: 9 additions & 0 deletions src/FluentCMS.Repositories/Abstractions/ISiteRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using FluentCMS.Entities;

namespace FluentCMS.Repositories.Abstractions;

public interface ISiteRepository : IGenericRepository<Site>
{
Task<Site?> GetByUrl(string url, CancellationToken cancellationToken = default);
Task<bool> CheckUrls(IList<string> urls, CancellationToken cancellationToken = default);
}
8 changes: 8 additions & 0 deletions src/FluentCMS.Repositories/Abstractions/IUserRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FluentCMS.Entities;

namespace FluentCMS.Repositories.Abstractions;

public interface IUserRepository : IGenericRepository<User>
{
Task<User?> GetByUsername(string username, CancellationToken cancellationToken = default);
}
20 changes: 20 additions & 0 deletions src/FluentCMS.Repositories/FluentCMS.Repositories.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LiteDB.Async" Version="0.1.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0-rc.2.23479.6" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.22.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FluentCMS.Entities\FluentCMS.Entities.csproj" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions src/FluentCMS.Repositories/LiteDb/LiteDbConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace FluentCMS.Repositories.LiteDb;

public class LiteDbOptions
{
public bool UseInMemory { get; set; } = false;
public string FilePath { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
17 changes: 17 additions & 0 deletions src/FluentCMS.Repositories/LiteDb/LiteDbContentTypeRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using FluentCMS.Entities;
using FluentCMS.Repositories.Abstractions;

namespace FluentCMS.Repositories.LiteDb;

public class LiteDbContentTypeRepository : LiteDbGenericRepository<ContentType>, IContentTypeRepository
{
public LiteDbContentTypeRepository(LiteDbContext dbContext) : base(dbContext)
{
}

public async Task<ContentType?> GetBySlug(string slug, CancellationToken cancellationToken = default)
{
var data = await Collection.FindOneAsync(x => x.Slug == slug);
return data;
}
}
114 changes: 114 additions & 0 deletions src/FluentCMS.Repositories/LiteDb/LiteDbGenericRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using FluentCMS.Entities;
using FluentCMS.Repositories.Abstractions;
using LiteDB;
using LiteDB.Async;

namespace FluentCMS.Repositories.LiteDb;

public class LiteDbGenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : IEntity
{
protected ILiteCollectionAsync<TEntity> Collection { get; }
protected ILiteCollectionAsync<BsonDocument> BsonCollection { get; }
protected LiteDbContext DbContext { get; private set; }

public LiteDbGenericRepository(LiteDbContext dbContext)
{
DbContext = dbContext;
Collection = dbContext.Database.GetCollection<TEntity>(GetCollectionName());
BsonCollection = dbContext.Database.GetCollection<BsonDocument>(GetCollectionName());
}

protected virtual string GetCollectionName()
{
return typeof(TEntity).Name;
}

public virtual async Task<TEntity?> Create(TEntity entity, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

entity.Id = Guid.NewGuid();

if (entity is IAuditEntity audit)
{
audit.CreatedAt = DateTime.UtcNow;
audit.LastUpdatedAt = DateTime.UtcNow;
}

await Collection.InsertAsync(entity);

return entity;
}

public virtual async Task<IEnumerable<TEntity>> CreateMany(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

foreach (var entity in entities)
entity.Id = Guid.NewGuid();

if (typeof(TEntity) is IAuditEntity)
foreach (var audit in entities.Cast<IAuditEntity>())
{
audit.CreatedAt = DateTime.UtcNow;
audit.LastUpdatedAt = DateTime.UtcNow;
}

await Collection.InsertBulkAsync(entities);
return entities;
}

public virtual async Task<TEntity?> Delete(Guid id, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

var entity = await GetById(id, cancellationToken);

if (entity is null)
return default;

var deleteCount = await Collection.DeleteManyAsync(x => x.Id == id);

return deleteCount == 1 ? entity : default;
}

public virtual async Task<IEnumerable<TEntity>> GetAll(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return await Collection.FindAllAsync();
}

// TODO: See the comment in IGenericRepository
//public virtual async Task<IEnumerable<TEntity>> GetAll(Expression<Func<TEntity, bool>> filter, CancellationToken cancellationToken = default)
//{
// cancellationToken.ThrowIfCancellationRequested();

// var result = await Collection.FindAsync(filter);
// return result.ToArray();
//}

public virtual async Task<TEntity?> GetById(Guid id, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var model = await Collection.FindByIdAsync(new BsonValue(id));
return model;
}

public virtual Task<IEnumerable<TEntity>> GetByIds(IEnumerable<Guid> ids, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return Collection.FindAsync(x => ids.Contains(x.Id));
}

public virtual async Task<TEntity?> Update(TEntity entity, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

if (entity is IAuditEntity audit)
audit.LastUpdatedAt = DateTime.UtcNow;

return await Collection.UpdateAsync(entity) ? entity : default;
}

}
33 changes: 33 additions & 0 deletions src/FluentCMS.Repositories/LiteDb/LiteDbOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using LiteDB.Async;
using Microsoft.Extensions.Options;

namespace FluentCMS.Repositories.LiteDb;

public class LiteDbContext
{
private readonly LiteDatabaseAsync _db = default!;
public LiteDatabaseAsync Database => _db;

public LiteDbContext(IOptions<LiteDbOptions> options)
{
if (options.Value is null)
throw new ArgumentNullException(nameof(options));

var liteDbConfig = options.Value;


if (string.IsNullOrWhiteSpace(liteDbConfig.FilePath) == false)
{
var connectionString = $"Filename={liteDbConfig.FilePath};Connection=shared";

if (string.IsNullOrWhiteSpace(liteDbConfig.Password) == false)
connectionString += ";Password=" + liteDbConfig.Password;

_db = new LiteDatabaseAsync(connectionString);
}
else
{
_db = new LiteDatabaseAsync(new MemoryStream());
}
}
}
18 changes: 18 additions & 0 deletions src/FluentCMS.Repositories/LiteDb/LiteDbPageRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using FluentCMS.Entities;
using FluentCMS.Repositories.Abstractions;

namespace FluentCMS.Repositories.LiteDb;

public class LiteDbPageRepository : LiteDbGenericRepository<Page>, IPageRepository
{
public LiteDbPageRepository(LiteDbContext dbContext) : base(dbContext)
{
}

public async Task<IEnumerable<Page>> GetBySiteId(Guid siteId, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

return await Collection.FindAsync(x => x.SiteId == siteId);
}
}
Loading