Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…r-kit into dotnet9
  • Loading branch information
iammukeshm committed Nov 22, 2024
2 parents a840da2 + df50461 commit fe987c0
Show file tree
Hide file tree
Showing 58 changed files with 1,778 additions and 198 deletions.
8 changes: 8 additions & 0 deletions src/Shared/Authorization/FshPermissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public static class FshPermissions
new("Delete Products", FshActions.Delete, FshResources.Products),
new("Export Products", FshActions.Export, FshResources.Products),

//brands
new("View Brands", FshActions.View, FshResources.Brands, IsBasic: true),
new("Search Brands", FshActions.Search, FshResources.Brands, IsBasic: true),
new("Create Brands", FshActions.Create, FshResources.Brands),
new("Update Brands", FshActions.Update, FshResources.Brands),
new("Delete Brands", FshActions.Delete, FshResources.Brands),
new("Export Brands", FshActions.Export, FshResources.Brands),

//todos
new("View Todos", FshActions.View, FshResources.Todos, IsBasic: true),
new("Search Todos", FshActions.Search, FshResources.Todos, IsBasic: true),
Expand Down
2 changes: 2 additions & 0 deletions src/api/framework/Core/Domain/AuditableEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class AuditableEntity<TId> : BaseEntity<TId>, IAuditable, ISoftDeletable
public Guid CreatedBy { get; set; }
public DateTimeOffset LastModified { get; set; }
public Guid? LastModifiedBy { get; set; }
public DateTimeOffset? Deleted { get; set; }
public Guid? DeletedBy { get; set; }
}

public abstract class AuditableEntity : AuditableEntity<Guid>
Expand Down
3 changes: 2 additions & 1 deletion src/api/framework/Core/Domain/Contracts/ISoftDeletable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

public interface ISoftDeletable
{

DateTimeOffset? Deleted { get; set; }
Guid? DeletedBy { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@ private List<Claim> GetClaims(FshUser user, string ipAddress) =>
new List<Claim>
{
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new(JwtRegisteredClaimNames.Sub, user.Id),
new(JwtRegisteredClaimNames.Email, user.Email!),
new(JwtRegisteredClaimNames.Name, user.FirstName ?? string.Empty),
new(ClaimTypes.NameIdentifier, user.Id),
new(ClaimTypes.Email, user.Email!),
new(ClaimTypes.Name, user.FirstName ?? string.Empty),
new(ClaimTypes.MobilePhone, user.PhoneNumber ?? string.Empty),
new(FshClaims.Fullname, $"{user.FirstName} {user.LastName}"),
new(ClaimTypes.Surname, user.LastName ?? string.Empty),
new(FshClaims.IpAddress, ipAddress),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;

namespace FSH.Framework.Infrastructure.Persistence;

internal static class ModelBuilderExtensions
{
public static ModelBuilder AppendGlobalQueryFilter<TInterface>(this ModelBuilder modelBuilder, Expression<Func<TInterface, bool>> filter)
{
// get a list of entities without a baseType that implement the interface TInterface
var entities = modelBuilder.Model.GetEntityTypes()
.Where(e => e.BaseType is null && e.ClrType.GetInterface(typeof(TInterface).Name) is not null)
.Select(e => e.ClrType);

foreach (var entity in entities)
{
var parameterType = Expression.Parameter(modelBuilder.Entity(entity).Metadata.ClrType);
var filterBody = ReplacingExpressionVisitor.Replace(filter.Parameters.Single(), parameterType, filter.Body);

// get the existing query filter
if (modelBuilder.Entity(entity).Metadata.GetQueryFilter() is { } existingFilter)
{
var existingFilterBody = ReplacingExpressionVisitor.Replace(existingFilter.Parameters.Single(), parameterType, existingFilter.Body);

// combine the existing query filter with the new query filter
filterBody = Expression.AndAlso(existingFilterBody, filterBody);
}

// apply the new query filter
modelBuilder.Entity(entity).HasQueryFilter(Expression.Lambda(filterBody, parameterType));
}

return modelBuilder;
}
}
6 changes: 6 additions & 0 deletions src/api/framework/Infrastructure/Persistence/FshDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public class FshDbContext(IMultiTenantContextAccessor<FshTenantInfo> multiTenant
private readonly IPublisher _publisher = publisher;
private readonly DatabaseOptions _settings = settings.Value;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// QueryFilters need to be applied before base.OnModelCreating
modelBuilder.AppendGlobalQueryFilter<ISoftDeletable>(s => s.Deleted == null);
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.EnableSensitiveDataLogging();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ private async Task PublishAuditTrailsAsync(DbContextEventData eventData)
var utcNow = timeProvider.GetUtcNow();
foreach (var entry in eventData.Context.ChangeTracker.Entries<IAuditable>().Where(x => x.State is EntityState.Added or EntityState.Deleted or EntityState.Modified).ToList())
{
var userId = currentUser.GetUserId();
var trail = new TrailDto()
{
Id = Guid.NewGuid(),
TableName = entry.Entity.GetType().Name,
UserId = currentUser.GetUserId(),
UserId = userId,
DateTime = utcNow
};

Expand Down Expand Up @@ -72,19 +73,26 @@ private async Task PublishAuditTrailsAsync(DbContextEventData eventData)
break;

case EntityState.Modified:
if (property.IsModified && property.OriginalValue == null && property.CurrentValue != null)
if (property.IsModified)
{
trail.ModifiedProperties.Add(propertyName);
trail.Type = TrailType.Delete;
trail.OldValues[propertyName] = property.OriginalValue;
trail.NewValues[propertyName] = property.CurrentValue;
}
else if (property.IsModified && property.OriginalValue?.Equals(property.CurrentValue) == false)
{
trail.ModifiedProperties.Add(propertyName);
trail.Type = TrailType.Update;
trail.OldValues[propertyName] = property.OriginalValue;
trail.NewValues[propertyName] = property.CurrentValue;
if (entry.Entity is ISoftDeletable && property.OriginalValue == null && property.CurrentValue != null)
{
trail.ModifiedProperties.Add(propertyName);
trail.Type = TrailType.Delete;
trail.OldValues[propertyName] = property.OriginalValue;
trail.NewValues[propertyName] = property.CurrentValue;
}
else if (property.OriginalValue?.Equals(property.CurrentValue) == false)
{
trail.ModifiedProperties.Add(propertyName);
trail.Type = TrailType.Update;
trail.OldValues[propertyName] = property.OriginalValue;
trail.NewValues[propertyName] = property.CurrentValue;
}
else
{
property.IsModified = false;
}
}
break;
}
Expand All @@ -106,9 +114,9 @@ public void UpdateEntities(DbContext? context)
if (context == null) return;
foreach (var entry in context.ChangeTracker.Entries<AuditableEntity>())
{
var utcNow = timeProvider.GetUtcNow();
if (entry.State is EntityState.Added or EntityState.Modified || entry.HasChangedOwnedEntities())
{
var utcNow = timeProvider.GetUtcNow();
if (entry.State == EntityState.Added)
{
entry.Entity.CreatedBy = currentUser.GetUserId();
Expand All @@ -117,6 +125,12 @@ public void UpdateEntities(DbContext? context)
entry.Entity.LastModifiedBy = currentUser.GetUserId();
entry.Entity.LastModified = utcNow;
}
if(entry.State is EntityState.Deleted && entry.Entity is ISoftDeletable softDelete)
{
softDelete.DeletedBy = currentUser.GetUserId();
softDelete.Deleted = utcNow;
entry.State = EntityState.Modified;
}
}
}
}
Expand Down

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace FSH.Starter.WebApi.Migrations.PostgreSQL.Catalog
{
/// <inheritdoc />
public partial class AddCatalogSchema : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "catalog");

migrationBuilder.CreateTable(
name: "Brands",
schema: "catalog",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
TenantId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
CreatedBy = table.Column<Guid>(type: "uuid", nullable: false),
LastModified = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
LastModifiedBy = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Brands", x => x.Id);
});

migrationBuilder.CreateTable(
name: "Products",
schema: "catalog",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
Description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
Price = table.Column<decimal>(type: "numeric", nullable: false),
BrandId = table.Column<Guid>(type: "uuid", nullable: true),
TenantId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
Created = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
CreatedBy = table.Column<Guid>(type: "uuid", nullable: false),
LastModified = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
LastModifiedBy = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Products", x => x.Id);
table.ForeignKey(
name: "FK_Products_Brands_BrandId",
column: x => x.BrandId,
principalSchema: "catalog",
principalTable: "Brands",
principalColumn: "Id");
});

migrationBuilder.CreateIndex(
name: "IX_Products_BrandId",
schema: "catalog",
table: "Products",
column: "BrandId");
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Products",
schema: "catalog");

migrationBuilder.DropTable(
name: "Brands",
schema: "catalog");
}
}
}
Loading

0 comments on commit fe987c0

Please sign in to comment.