using Microsoft.Extensions.Logging; using Raven.Client.Documents; using Raven.Client.Documents.Session; namespace zero.Architecture; public class BlueprintInterceptor : Interceptor, IBlueprintInterceptor { protected IZeroContext Context { get; set; } protected IZeroStore Store { get; set; } protected ILogger Logger { get; set; } protected IBlueprintService BlueprintService { get; set; } IList apps; string configuredZeroDatabase; public BlueprintInterceptor(IZeroContext context, IZeroStore store, ILogger logger, IBlueprintService blueprintService) { Gravity = -1; Context = context; Store = store; Logger = logger; BlueprintService = blueprintService; configuredZeroDatabase = context.Options.For().Database; } /// /// Only run when operations are on the zero database /// public override bool CanHandle(InterceptorParameters args, Type modelType) => args.Context.Store.ResolvedDatabase == configuredZeroDatabase; /// public override Task Created(InterceptorParameters args, ZeroEntity model) => Saved(args, model, false); /// public override Task Updated(InterceptorParameters args, ZeroEntity model) => Saved(args, model, true); /// public async Task Saved(InterceptorParameters args, ZeroEntity model, bool update = false) { if (!BlueprintService.TryGetBlueprint(model, out Blueprint blueprint)) { blueprint = new DefaultShallowBlueprint(model.GetType()); } int count = 0; foreach (Application app in await GetApplications()) { using ZeroContextScope scope = Context.CreateScope(app); IZeroDocumentSession session = scope.Store.Session(scope.Database); ZeroEntity child = await session.LoadAsync(model.Id); if (child == null) { child = ObjectCopycat.Clone(model); child.Blueprint = new() { TargetId = model.Id }; } else { blueprint.Apply(model, child); } count += 1; InterceptorInstruction interceptor = update ? args.Interceptors.ForUpdate(model) : args.Interceptors.ForCreate(model); interceptor.Filter(x => x is not IBlueprintInterceptor); await interceptor.Start(args.Operations); await session.StoreAsync(child); await interceptor.Complete(); await session.SaveChangesAsync(); } Logger.LogDebug("Blueprint: Synced {count} children for {name} ({id})", count, model.Name, model.Id); } /// public override async Task Deleted(InterceptorParameters args, ZeroEntity model) { if (!BlueprintService.TryGetBlueprint(model, out Blueprint blueprint)) { return; } int count = 0; foreach (Application app in await GetApplications()) { using ZeroContextScope scope = Context.CreateScope(app); IZeroDocumentSession session = scope.Store.Session(scope.Database); count += 1; InterceptorInstruction interceptor = args.Interceptors.ForDelete(model); interceptor.Filter(x => x is not IBlueprintInterceptor); await interceptor.Start(args.Operations); session.Delete(model.Id); await interceptor.Complete(); await session.SaveChangesAsync(); } Logger.LogDebug("Blueprint: Deleted {count} children for {name} ({id})", count, model.Name, model.Id); } /// /// Get all applications to choose from /// async Task> GetApplications() { if (apps != null) { return apps; } IAsyncDocumentSession session = Store.Session(global: true); apps = await session.Query().ToListAsync(); return apps; } }