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

RavenDB #2144

Open
wants to merge 13 commits into
base: dev
Choose a base branch
from
Open

RavenDB #2144

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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
################################################################################

/.vs
.DS_Store
3 changes: 1 addition & 2 deletions src/Backend/FluentCMS.Entities/AuditableEntityLog.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
namespace FluentCMS.Entities;

public class AuditableEntityLog
public class AuditableEntityLog : Entity
{
public Guid Id { get; set; }
public string EntityType { get; set; } = default!;
public Guid EntityId { get; set; }
public string Action { get; set; } = default!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ public class PluginDefinitionCreateRequest
{
public string Name { get; set; } = default!;
public string Category { get; set; } = default!;
public string Assembly { get; set; } = default!;
public string? Description { get; set; }
public string Type { get; set; } = default!;
public IEnumerable<PluginDefinitionType> Types { get; set; } = [];
public bool Locked { get; set; } = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace FluentCMS.Repositories.RavenDB;

public class ApiTokenRepository(IRavenDBContext dbContext, IApiExecutionContext apiExecutionContext) : AuditableEntityRepository<ApiToken>(dbContext, apiExecutionContext), IApiTokenRepository
{
public async Task<ApiToken?> GetByKey(string apiKey, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

using (var session = Store.OpenAsyncSession())
{
var entity = await session.Query<RavenEntity<ApiToken>>().SingleOrDefaultAsync(x => x.Data.Key == apiKey, cancellationToken);

return entity?.Data;
}
}

public async Task<ApiToken?> GetByName(string name, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

using (var session = Store.OpenAsyncSession())
{
var entity = await session.Query<RavenEntity<ApiToken>>().SingleOrDefaultAsync(x => x.Data.Name == name, cancellationToken);

return entity?.Data;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace FluentCMS.Repositories.RavenDB;

public abstract class AuditableEntityRepository<TEntity>(IRavenDBContext dbContext, IApiExecutionContext apiExecutionContext) : EntityRepository<TEntity>(dbContext), IAuditableEntityRepository<TEntity> where TEntity : IAuditableEntity
{
protected readonly IApiExecutionContext ApiExecutionContext = apiExecutionContext;

public override async Task<TEntity?> Create(TEntity entity, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
SetAuditableFieldsForCreate(entity);
return await base.Create(entity, cancellationToken);
}

public override async Task<IEnumerable<TEntity>> CreateMany(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var entity in entities)
SetAuditableFieldsForCreate(entity);

return await base.CreateMany(entities, cancellationToken);
}

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

using (var session = Store.OpenAsyncSession())
{
var id = entity.Id;
var dbEntity = await session.Query<RavenEntity<TEntity>>().SingleOrDefaultAsync(x => x.Data.Id == id, cancellationToken);
if (dbEntity == null)
{
SetAuditableFieldsForCreate(entity);

dbEntity = new RavenEntity<TEntity>(entity);

await session.StoreAsync(dbEntity, cancellationToken);
}
else
{
entity.CopyProperties(dbEntity.Data);

SetAuditableFieldsForUpdate(dbEntity.Data);
}

await session.SaveChangesAsync(cancellationToken);

return dbEntity.Data;
}
}

protected void SetAuditableFieldsForCreate(TEntity entity)
{
if (entity.Id == Guid.Empty)
{
entity.Id = Guid.NewGuid();
}

entity.CreatedAt = DateTime.UtcNow;
entity.CreatedBy = ApiExecutionContext.Username;
entity.ModifiedAt = entity.CreatedAt;
entity.ModifiedBy = entity.CreatedBy;
}

protected void SetAuditableFieldsForUpdate(TEntity entity)
{
if (entity.Id == Guid.Empty)
{
entity.Id = Guid.NewGuid();
}

entity.ModifiedAt = DateTime.UtcNow;
entity.ModifiedBy = ApiExecutionContext.Username;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace FluentCMS.Repositories.RavenDB;

public class BlockRepository(IRavenDBContext RavenDbContext, IApiExecutionContext apiExecutionContext) : SiteAssociatedRepository<Block>(RavenDbContext, apiExecutionContext), IBlockRepository
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace FluentCMS.Repositories.RavenDB;

public class ContentRepository(IRavenDBContext dbContext, IApiExecutionContext apiExecutionContext) : AuditableEntityRepository<Content>(dbContext, apiExecutionContext), IContentRepository
{
public async Task<IEnumerable<Content>> GetAll(Guid contentTypeId, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

using (var session = Store.OpenAsyncSession())
{
var entities = await session.Query<RavenEntity<Content>>()
.Where(x => x.Data.TypeId == contentTypeId)
.Select(x => x.Data)
.ToListAsync(cancellationToken);

return entities.AsEnumerable();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace FluentCMS.Repositories.RavenDB;

public class ContentTypeRepository(IRavenDBContext dbContext, IApiExecutionContext apiExecutionContext) : AuditableEntityRepository<ContentType>(dbContext, apiExecutionContext), IContentTypeRepository
{
public async Task<ContentType?> GetBySlug(string contentTypeSlug, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

using (var session = Store.OpenAsyncSession())
{
var entity = await session.Query<RavenEntity<ContentType>>().SingleOrDefaultAsync(x => x.Data.Slug == contentTypeSlug, cancellationToken);

return entity?.Data;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using Raven.Client.Documents.Linq;

namespace FluentCMS.Repositories.RavenDB;

public abstract class EntityRepository<TEntity> : IEntityRepository<TEntity> where TEntity : IEntity
{
protected readonly IDocumentStore Store;

public EntityRepository(IRavenDBContext RavenDbContext)
{
Store = RavenDbContext.Store;

// Ensure index on Id field
// var indexKeysDefinition = Builders<TEntity>.IndexKeys.Ascending(x => x.Id);
// Collection.Indexes.CreateOne(new CreateIndexModel<TEntity>(indexKeysDefinition));
}

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

using (var session = Store.OpenAsyncSession())
{
var entities = await session.Query<RavenEntity<TEntity>>()
.Select(x => x.Data)
.ToListAsync(cancellationToken);

return entities.AsEnumerable();
}
}

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

using (var session = Store.OpenAsyncSession())
{
var entity = await session.Query<RavenEntity<TEntity>>().SingleOrDefaultAsync(x => x.Data.Id == id, cancellationToken);

return entity.Data;
}
}

public virtual async Task<IEnumerable<TEntity>> GetByIds(IEnumerable<Guid> ids, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

using (var session = Store.OpenAsyncSession())
{
var entities = await session.Query<RavenEntity<TEntity>>().Where(x => ids.Contains(x.Data.Id))
.Select(x => x.Data)
.ToListAsync(cancellationToken);

return entities.AsEnumerable();
}
}

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

using (var session = Store.OpenAsyncSession())
{
if (entity.Id == Guid.Empty)
{
entity.Id = Guid.NewGuid();
}

await session.StoreAsync(new RavenEntity<TEntity>(entity), cancellationToken);

await session.SaveChangesAsync(cancellationToken);
}

return entity;
}

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

using (var session = Store.OpenAsyncSession())
{
foreach (var entity in entities)
{
cancellationToken.ThrowIfCancellationRequested();

if (entity.Id == Guid.Empty)
{
entity.Id = Guid.NewGuid();
}

await session.StoreAsync(new RavenEntity<TEntity>(entity), cancellationToken);
}

await session.SaveChangesAsync(cancellationToken);
}

return entities;
}

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

using (var session = Store.OpenAsyncSession())
{
var id = entity.Id; // Needs to be extracted to guid to avoid type casts in query.

var dbEntity = await session.Query<RavenEntity<TEntity>>().SingleOrDefaultAsync(x => x.Data.Id == id, cancellationToken);
if (dbEntity == null)
{
if (entity.Id == Guid.Empty)
{
entity.Id = Guid.NewGuid();
}

dbEntity = new RavenEntity<TEntity>(entity);

await session.StoreAsync(dbEntity, cancellationToken);
}
else
{
entity.CopyProperties(dbEntity.Data);
}

await session.SaveChangesAsync(cancellationToken);

return dbEntity.Data;
}
}

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

using (var session = Store.OpenAsyncSession())
{
var entity = await session.Query<RavenEntity<TEntity>>().SingleOrDefaultAsync(x => x.Data.Id == id, cancellationToken);

session.Delete(entity);

await session.SaveChangesAsync(cancellationToken);

return entity.Data; // A bit strange to return to object we just deleted.
}
}

public virtual async Task<IEnumerable<TEntity>> DeleteMany(IEnumerable<Guid> ids, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

using (var session = Store.OpenAsyncSession())
{
var entities = await session.Query<RavenEntity<TEntity>>().Where(x => ids.Contains(x.Data.Id)).ToListAsync(cancellationToken);

foreach (var entity in entities)
{
cancellationToken.ThrowIfCancellationRequested();

session.Delete(entity);
}

await session.SaveChangesAsync(cancellationToken);

return entities.Select(x => x.Data);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FluentCMS.Repositories.RavenDB;

public class FileRepository : AuditableEntityRepository<File>, IFileRepository
{
public FileRepository(IRavenDBContext RavenDbContext, IApiExecutionContext apiExecutionContext) : base(RavenDbContext, apiExecutionContext)
{
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="RavenDB.Client" Version="6.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FluentCMS.Repositories.Abstractions\FluentCMS.Repositories.Abstractions.csproj" />
<ProjectReference Include="..\..\FluentCMS.Entities\FluentCMS.Entities.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace FluentCMS.Repositories.RavenDB;

public class FolderRepository : AuditableEntityRepository<Folder>, IFolderRepository
{
public FolderRepository(IRavenDBContext RavenDbContext, IApiExecutionContext apiExecutionContext) : base(RavenDbContext, apiExecutionContext)
{
}
}
Loading