mirror of https://github.com/Squidex/squidex.git
Browse Source
* Started with a few repositories. * Get rid unnecessary allocations. * New repositories and cancellation token improvements. * First untested implementation. * Fix infrastructure tests. * Warnings fixed. * All tests green again. * New tests. * Better cancellation. * More tests. * Backup fix. * Markdown everything * A few more tests and fixes. * Fix app deletion flag. * Provide app to client creator. * Started with migration. * Temp. * Minor change to rename app deletion. * Toggle for deleter. * Fix build.pull/768/head
committed by
GitHub
544 changed files with 6609 additions and 4485 deletions
@ -1,189 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Apps.Indexes; |
|||
using Squidex.Domain.Apps.Entities.Rules.Indexes; |
|||
using Squidex.Domain.Apps.Entities.Schemas.Indexes; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Domain.Apps.Events.Rules; |
|||
using Squidex.Domain.Apps.Events.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Migrations; |
|||
|
|||
namespace Migrations.Migrations |
|||
{ |
|||
public class PopulateGrainIndexes : IMigration |
|||
{ |
|||
private readonly IAppsIndex indexApps; |
|||
private readonly IRulesIndex indexRules; |
|||
private readonly ISchemasIndex indexSchemas; |
|||
private readonly IEventDataFormatter eventDataFormatter; |
|||
private readonly IEventStore eventStore; |
|||
|
|||
public PopulateGrainIndexes(IAppsIndex indexApps, IRulesIndex indexRules, ISchemasIndex indexSchemas, |
|||
IEventDataFormatter eventDataFormatter, |
|||
IEventStore eventStore) |
|||
{ |
|||
this.indexApps = indexApps; |
|||
this.indexRules = indexRules; |
|||
this.indexSchemas = indexSchemas; |
|||
this.eventDataFormatter = eventDataFormatter; |
|||
this.eventStore = eventStore; |
|||
} |
|||
|
|||
public Task UpdateAsync(CancellationToken ct) |
|||
{ |
|||
return Task.WhenAll( |
|||
RebuildAppIndexes(ct), |
|||
RebuildRuleIndexes(ct), |
|||
RebuildSchemaIndexes(ct)); |
|||
} |
|||
|
|||
private async Task RebuildAppIndexes(CancellationToken ct) |
|||
{ |
|||
var appsByName = new Dictionary<string, DomainId>(); |
|||
var appsByUser = new Dictionary<string, HashSet<DomainId>>(); |
|||
|
|||
bool HasApp(NamedId<DomainId> appId, bool consistent, out DomainId id) |
|||
{ |
|||
return appsByName!.TryGetValue(appId.Name, out id) && (!consistent || id == appId.Id); |
|||
} |
|||
|
|||
HashSet<DomainId> Index(string contributorId) |
|||
{ |
|||
return appsByUser!.GetOrAddNew(contributorId); |
|||
} |
|||
|
|||
void RemoveApp(NamedId<DomainId> appId, bool consistent) |
|||
{ |
|||
if (HasApp(appId, consistent, out var id)) |
|||
{ |
|||
foreach (var apps in appsByUser!.Values) |
|||
{ |
|||
apps.Remove(id); |
|||
} |
|||
|
|||
appsByName!.Remove(appId.Name); |
|||
} |
|||
} |
|||
|
|||
await foreach (var storedEvent in eventStore.QueryAllAsync("^app\\-", ct: ct)) |
|||
{ |
|||
var @event = eventDataFormatter.ParseIfKnown(storedEvent); |
|||
|
|||
if (@event != null) |
|||
{ |
|||
switch (@event.Payload) |
|||
{ |
|||
case AppCreated created: |
|||
{ |
|||
RemoveApp(created.AppId, false); |
|||
|
|||
appsByName[created.Name] = created.AppId.Id; |
|||
break; |
|||
} |
|||
|
|||
case AppContributorAssigned contributorAssigned: |
|||
{ |
|||
if (HasApp(contributorAssigned.AppId, true, out _)) |
|||
{ |
|||
Index(contributorAssigned.ContributorId).Add(contributorAssigned.AppId.Id); |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
case AppContributorRemoved contributorRemoved: |
|||
Index(contributorRemoved.ContributorId).Remove(contributorRemoved.AppId.Id); |
|||
break; |
|||
case AppArchived archived: |
|||
RemoveApp(archived.AppId, true); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
await indexApps.RebuildAsync(appsByName); |
|||
|
|||
foreach (var (contributorId, apps) in appsByUser) |
|||
{ |
|||
await indexApps.RebuildByContributorsAsync(contributorId, apps); |
|||
} |
|||
} |
|||
|
|||
private async Task RebuildRuleIndexes(CancellationToken ct) |
|||
{ |
|||
var rulesByApp = new Dictionary<DomainId, HashSet<DomainId>>(); |
|||
|
|||
HashSet<DomainId> Index(RuleEvent @event) |
|||
{ |
|||
return rulesByApp!.GetOrAddNew(@event.AppId.Id); |
|||
} |
|||
|
|||
await foreach (var storedEvent in eventStore.QueryAllAsync("^rule\\-", ct: ct)) |
|||
{ |
|||
var @event = eventDataFormatter.ParseIfKnown(storedEvent); |
|||
|
|||
if (@event != null) |
|||
{ |
|||
switch (@event.Payload) |
|||
{ |
|||
case RuleCreated created: |
|||
Index(created).Add(created.RuleId); |
|||
break; |
|||
case RuleDeleted deleted: |
|||
Index(deleted).Remove(deleted.RuleId); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
foreach (var (appId, rules) in rulesByApp) |
|||
{ |
|||
await indexRules.RebuildAsync(appId, rules); |
|||
} |
|||
} |
|||
|
|||
private async Task RebuildSchemaIndexes(CancellationToken ct) |
|||
{ |
|||
var schemasByApp = new Dictionary<DomainId, Dictionary<string, DomainId>>(); |
|||
|
|||
Dictionary<string, DomainId> Index(SchemaEvent @event) |
|||
{ |
|||
return schemasByApp!.GetOrAddNew(@event.AppId.Id); |
|||
} |
|||
|
|||
await foreach (var storedEvent in eventStore.QueryAllAsync("^schema\\-", ct: ct)) |
|||
{ |
|||
var @event = eventDataFormatter.ParseIfKnown(storedEvent); |
|||
|
|||
if (@event != null) |
|||
{ |
|||
switch (@event.Payload) |
|||
{ |
|||
case SchemaCreated created: |
|||
Index(created)[created.SchemaId.Name] = created.SchemaId.Id; |
|||
break; |
|||
case SchemaDeleted deleted: |
|||
Index(deleted).Remove(deleted.SchemaId.Name); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
foreach (var (appId, schemas) in schemasByApp) |
|||
{ |
|||
await indexSchemas.RebuildAsync(appId, schemas); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Migrations; |
|||
|
|||
namespace Migrations.Migrations |
|||
{ |
|||
public sealed class RebuildRules : IMigration |
|||
{ |
|||
private readonly Rebuilder rebuilder; |
|||
private readonly RebuildOptions rebuildOptions; |
|||
|
|||
public RebuildRules(Rebuilder rebuilder, |
|||
IOptions<RebuildOptions> rebuildOptions) |
|||
{ |
|||
this.rebuilder = rebuilder; |
|||
this.rebuildOptions = rebuildOptions.Value; |
|||
} |
|||
|
|||
public Task UpdateAsync( |
|||
CancellationToken ct) |
|||
{ |
|||
return rebuilder.RebuildRulesAsync(rebuildOptions.BatchSize, ct); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Migrations; |
|||
|
|||
namespace Migrations.Migrations |
|||
{ |
|||
public sealed class RebuildSchemas : IMigration |
|||
{ |
|||
private readonly Rebuilder rebuilder; |
|||
private readonly RebuildOptions rebuildOptions; |
|||
|
|||
public RebuildSchemas(Rebuilder rebuilder, |
|||
IOptions<RebuildOptions> rebuildOptions) |
|||
{ |
|||
this.rebuilder = rebuilder; |
|||
this.rebuildOptions = rebuildOptions.Value; |
|||
} |
|||
|
|||
public Task UpdateAsync( |
|||
CancellationToken ct) |
|||
{ |
|||
return rebuilder.RebuildSchemasAsync(rebuildOptions.BatchSize, ct); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Migrations; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Migrations.OldEvents |
|||
{ |
|||
[EventType(nameof(AppArchived))] |
|||
public sealed class AppArchived : AppEvent, IMigrated<IEvent> |
|||
{ |
|||
public IEvent Migrate() |
|||
{ |
|||
return SimpleMapper.Map(this, new AppDeleted()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
using Squidex.Domain.Apps.Entities.Apps.DomainObject; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Apps |
|||
{ |
|||
public sealed class MongoAppEntity : MongoState<AppDomainObject.State> |
|||
{ |
|||
[BsonRequired] |
|||
[BsonElement("_an")] |
|||
public string IndexedName { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement("_ui")] |
|||
public string[] IndexedUserIds { get; set; } |
|||
|
|||
[BsonRequired] |
|||
[BsonElement("_dl")] |
|||
public bool IndexedDeleted { get; set; } |
|||
|
|||
public override void Prepare() |
|||
{ |
|||
var users = new HashSet<string> |
|||
{ |
|||
Document.CreatedBy.Identifier |
|||
}; |
|||
|
|||
users.AddRange(Document.Contributors.Keys); |
|||
users.AddRange(Document.Clients.Keys); |
|||
|
|||
IndexedUserIds = users.ToArray(); |
|||
IndexedDeleted = Document.IsDeleted; |
|||
IndexedName = Document.Name; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using Newtonsoft.Json; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.DomainObject; |
|||
using Squidex.Domain.Apps.Entities.Apps.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.MongoDb; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Apps |
|||
{ |
|||
public sealed class MongoAppRepository : MongoSnapshotStoreBase<AppDomainObject.State, MongoAppEntity>, IAppRepository, IDeleter |
|||
{ |
|||
public MongoAppRepository(IMongoDatabase database, JsonSerializer jsonSerializer) |
|||
: base(database, jsonSerializer) |
|||
{ |
|||
} |
|||
|
|||
protected override Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection, |
|||
CancellationToken ct) |
|||
{ |
|||
return collection.Indexes.CreateManyAsync(new[] |
|||
{ |
|||
new CreateIndexModel<MongoAppEntity>( |
|||
Index |
|||
.Ascending(x => x.IndexedName)), |
|||
new CreateIndexModel<MongoAppEntity>( |
|||
Index |
|||
.Ascending(x => x.IndexedUserIds)) |
|||
}, ct); |
|||
} |
|||
|
|||
Task IDeleter.DeleteAppAsync(IAppEntity app, |
|||
CancellationToken ct) |
|||
{ |
|||
return Collection.DeleteManyAsync(Filter.Eq(x => x.DocumentId, app.Id), ct); |
|||
} |
|||
|
|||
public async Task<Dictionary<string, DomainId>> QueryIdsAsync(string contributorId, |
|||
CancellationToken ct = default) |
|||
{ |
|||
using (Telemetry.Activities.StartActivity("MongoAppRepository/QueryIdsAsync")) |
|||
{ |
|||
var find = Collection.Find(x => x.IndexedUserIds.Contains(contributorId) && !x.IndexedDeleted); |
|||
|
|||
return await QueryAsync(find, ct); |
|||
} |
|||
} |
|||
|
|||
public async Task<Dictionary<string, DomainId>> QueryIdsAsync(IEnumerable<string> names, |
|||
CancellationToken ct = default) |
|||
{ |
|||
using (Telemetry.Activities.StartActivity("MongoAppRepository/QueryAsync")) |
|||
{ |
|||
var find = Collection.Find(x => names.Contains(x.IndexedName) && !x.IndexedDeleted); |
|||
|
|||
return await QueryAsync(find, ct); |
|||
} |
|||
} |
|||
|
|||
private static async Task<Dictionary<string, DomainId>> QueryAsync(IFindFluent<MongoAppEntity, MongoAppEntity> find, |
|||
CancellationToken ct) |
|||
{ |
|||
var entities = await find.Only(x => x.DocumentId, x => x.IndexedName).ToListAsync(ct); |
|||
|
|||
return entities.Select(x => |
|||
{ |
|||
var indexedId = DomainId.Create(x["_id"].AsString); |
|||
var indexedName = x["_an"].AsString; |
|||
|
|||
return new { indexedName, indexedId }; |
|||
}).ToDictionary(x => x.indexedName, x => x.indexedId); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue