Skip to content

Commit c6e1bf5

Browse files
Started implementing generic draft entities.
1 parent 0e3eb61 commit c6e1bf5

File tree

12 files changed

+273
-6
lines changed

12 files changed

+273
-6
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Text.Json;
2+
using DotNetElements.AppFramework.Abstractions.Entity;
3+
4+
namespace DotNetElements.AppFramework.Abstractions.Drafts;
5+
6+
public sealed class Draft<T> : AuditedEntity<Guid>, IUpdateFrom<DraftModel<T>>
7+
{
8+
public Guid OwnerId { get; private init; }
9+
public int Version { get; private set; }
10+
public string Content { get; private set; }
11+
public string? Name { get; private init; }
12+
13+
public Draft(Guid ownerId, T content, string? name)
14+
{
15+
OwnerId = ownerId;
16+
Name = name;
17+
18+
Content = JsonSerializer.Serialize(content);
19+
Version = 1;
20+
}
21+
22+
#nullable disable
23+
private Draft() { } // EF Core constructor
24+
#nullable enable
25+
26+
public void Update(DraftModel<T> from)
27+
{
28+
string updatedContent = JsonSerializer.Serialize(from.Content);
29+
30+
if (Content == updatedContent)
31+
return;
32+
33+
Content = updatedContent;
34+
Version++;
35+
}
36+
}
37+
38+
public static class DraftMapper
39+
{
40+
public static Draft<T> MapToEntity<T>(this DraftModel<T> model)
41+
{
42+
return new Draft<T>(model.OwnerId, model.Content, model.Name);
43+
}
44+
45+
public static DraftModel<T> MapToModel<T>(this Draft<T> entity)
46+
{
47+
return new DraftModel<T>
48+
{
49+
Id = entity.Id,
50+
OwnerId = entity.OwnerId,
51+
Version = entity.Version,
52+
Content = JsonSerializer.Deserialize<T>(entity.Content)!,
53+
Name = entity.Name
54+
};
55+
}
56+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using DotNetElements.AppFramework.Abstractions.Model;
2+
3+
namespace DotNetElements.AppFramework.Abstractions.Drafts;
4+
5+
public sealed class DraftModel<T> : Model<Guid>
6+
{
7+
public required Guid OwnerId { get; init; }
8+
public required int Version { get; init; }
9+
public required T Content { get; init; }
10+
public string? Name { get; init; }
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace DotNetElements.AppFramework.Abstractions.Drafts;
2+
3+
public static class DraftsRoutes
4+
{
5+
public static string BaseUrl(string draftsEndpoint) => $"{draftsEndpoint}/drafts";
6+
public static string CreateOrUpdate(string draftsEndpoint) => BaseUrl(draftsEndpoint);
7+
public static string Delete(string draftsEndpoint) => $"{BaseUrl(draftsEndpoint)}/{{id}}";
8+
public static string GetById(string draftsEndpoint) => $"{BaseUrl(draftsEndpoint)}/{{id}}";
9+
public static string GetDetails(string draftsEndpoint) => $"{BaseUrl(draftsEndpoint)}/{{id}}/details";
10+
11+
public static string GetCreateOrUpdateUrl(string draftsEndpoint) => BaseUrl(draftsEndpoint);
12+
public static string GetDeleteUrl(string draftsEndpoint, Guid id) => $"{BaseUrl(draftsEndpoint)}/{id}";
13+
public static string GetByIdUrl(string draftsEndpoint, Guid id) => $"{BaseUrl(draftsEndpoint)}/{id}";
14+
public static string GetDetailsUrl(string draftsEndpoint, Guid id) => $"{BaseUrl(draftsEndpoint)}/{id}/details";
15+
}

src/DotNetElements.AppFramework.Abstractions/Model/ModelBase.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System.Runtime.CompilerServices;
2-
using System.Runtime.Serialization;
3-
4-
namespace DotNetElements.AppFramework.Abstractions.Model;
1+
namespace DotNetElements.AppFramework.Abstractions.Model;
52

63
public interface IModel<TKey> : IHasKey<TKey>
74
where TKey : notnull, IEquatable<TKey>;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Linq.Expressions;
2+
using DotNetElements.AppFramework.Abstractions.Drafts;
3+
4+
namespace DotNetElements.AppFramework.AspNet.Drafts;
5+
6+
public static class DraftModelBuilderExtensions
7+
{
8+
public static void ConfigureDrafts<TOwner, TContent>(this ModelBuilder modelBuilder, Expression<Func<TOwner, IEnumerable<Draft<TContent>>?>> navigationExpression)
9+
where TOwner : Entity<Guid>
10+
where TContent : class
11+
{
12+
modelBuilder.Entity<TOwner>()
13+
.HasMany(navigationExpression)
14+
.WithOne()
15+
.HasForeignKey(e => e.OwnerId)
16+
.IsRequired();
17+
}
18+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using DotNetElements.AppFramework.Abstractions.Auth;
2+
using DotNetElements.AppFramework.Abstractions.Drafts;
3+
using DotNetElements.AppFramework.Abstractions.Model;
4+
5+
namespace DotNetElements.AppFramework.AspNet.Drafts;
6+
7+
internal sealed class DraftsService<TContent, TDbContext> : ModuleService<TDbContext>
8+
where TContent : class
9+
where TDbContext : DbContext, IDbSetDraft<TContent>
10+
{
11+
public DraftsService(TDbContext dbContext, ICurrentUserProvider currentUserProvider, TimeProvider timeProvider)
12+
: base(dbContext, currentUserProvider, timeProvider)
13+
{
14+
}
15+
16+
public async Task<CrudResult<DraftModel<TContent>>> CreateOrUpdateDraftAsync(DraftModel<TContent> model)
17+
{
18+
Draft<TContent>? existingEntity = await DbContext.Drafts
19+
.FindAsync(model.Id);
20+
21+
if (existingEntity is null)
22+
return await CreateDraftAsync(model);
23+
else
24+
return await UpdateDraftAsync(existingEntity, model);
25+
}
26+
27+
public async Task<CrudResult> DeleteDraftByIdAsync(Guid id)
28+
{
29+
Draft<TContent>? existingEntity = await DbContext.Drafts
30+
.FindAsync(id);
31+
32+
if (existingEntity is null)
33+
return Fail(CrudError.NotFound);
34+
35+
DbContext.Drafts.Remove(existingEntity);
36+
37+
await DbContext.SaveChangesAsync();
38+
39+
return CrudResult.Ok(); // todo change to Ok() if DotNetElements.Result is updated
40+
}
41+
42+
public async Task<CrudResult<DraftModel<TContent>>> GetDraftById(Guid id)
43+
{
44+
Draft<TContent>? existingEntity = await DbContext.Drafts
45+
.FindAsync(id);
46+
47+
if (existingEntity is null)
48+
return Fail(CrudError.NotFound);
49+
50+
return existingEntity.MapToModel();
51+
}
52+
53+
public Task<CrudResult<AuditedModelDetails>> GetDraftAuditDetailsById(Guid id)
54+
{
55+
return GetAuditedDetailsByEntityId<Draft<TContent>, Guid>(id);
56+
}
57+
58+
private async Task<CrudResult<DraftModel<TContent>>> CreateDraftAsync(DraftModel<TContent> model)
59+
{
60+
Draft<TContent> newDraft = model.MapToEntity();
61+
62+
await AttachAndSaveChangesAsync(newDraft);
63+
64+
return newDraft.MapToModel();
65+
}
66+
67+
private async Task<CrudResult<DraftModel<TContent>>> UpdateDraftAsync(Draft<TContent> existingEntity, DraftModel<TContent> model)
68+
{
69+
existingEntity.Update(model);
70+
71+
await DbContext.SaveChangesAsync();
72+
73+
return existingEntity.MapToModel();
74+
}
75+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using DotNetElements.AppFramework.Abstractions.Drafts;
2+
3+
namespace DotNetElements.AppFramework.AspNet.Drafts;
4+
5+
public interface IDbSetDraft<TContent>
6+
where TContent : class
7+
{
8+
public DbSet<Draft<TContent>> Drafts { get; }
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.AspNetCore.Builder;
2+
3+
namespace DotNetElements.AppFramework.AspNet.Drafts;
4+
5+
public static class WebApplicationBuilderExtensions
6+
{
7+
public static WebApplicationBuilder AddDrafts<TContent, TDbContext>(this WebApplicationBuilder builder)
8+
where TContent : class
9+
where TDbContext : DbContext, IDbSetDraft<TContent>
10+
{
11+
builder.Services.AddModuleService<DraftsService<TContent, TDbContext>, TDbContext>();
12+
13+
return builder;
14+
}
15+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using DotNetElements.AppFramework.Abstractions.Drafts;
2+
using DotNetElements.AppFramework.Abstractions.Model;
3+
using DotNetElements.AppFramework.AspNet.ResultExtensions;
4+
using Microsoft.AspNetCore.Builder;
5+
6+
namespace DotNetElements.AppFramework.AspNet.Drafts;
7+
8+
public static class WebApplicationExtensions
9+
{
10+
public static WebApplication UseDrafts<TContent, TDbContext>(WebApplication app, string draftsEndpoint)
11+
where TContent : class
12+
where TDbContext : DbContext, IDbSetDraft<TContent>
13+
{
14+
// Create or update
15+
app.MapPut(DraftsRoutes.CreateOrUpdate(draftsEndpoint), async (DraftModel<TContent> model, DraftsService<TContent, TDbContext> draftsService) =>
16+
{
17+
CrudResult<DraftModel<TContent>> updateResult = await draftsService.CreateOrUpdateDraftAsync(model);
18+
19+
return updateResult.MapToHttpResult();
20+
});
21+
22+
// Delete
23+
app.MapDelete(DraftsRoutes.Delete(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
24+
{
25+
CrudResult deleteResult = await draftsService.DeleteDraftByIdAsync(id);
26+
27+
return deleteResult.MapToHttpResult();
28+
});
29+
30+
// Get by ID
31+
app.MapGet(DraftsRoutes.GetById(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
32+
{
33+
CrudResult<DraftModel<TContent>> result = await draftsService.GetDraftById(id);
34+
35+
return result.MapToHttpResult();
36+
});
37+
38+
// Get Audit Details
39+
app.MapGet(DraftsRoutes.GetDetails(draftsEndpoint), async (Guid id, DraftsService<TContent, TDbContext> draftsService) =>
40+
{
41+
CrudResult<AuditedModelDetails> auditResult = await draftsService.GetDraftAuditDetailsById(id);
42+
43+
return auditResult.MapToHttpResult();
44+
});
45+
46+
return app;
47+
}
48+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using DotNetElements.AppFramework.Abstractions.Drafts;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
4+
5+
namespace DotNetElements.AppFramework.SqlServer;
6+
7+
public sealed class DraftEntityConfiguration<T> : IEntityTypeConfiguration<Draft<T>>
8+
{
9+
public void Configure(EntityTypeBuilder<Draft<T>> builder)
10+
{
11+
builder
12+
.Property(entity => entity.Name)
13+
.HasMaxLength(255);
14+
}
15+
}

0 commit comments

Comments
 (0)