Browse Source

WIP: Feature/string-identifiers (#524)

* Global ids for all elements
pull/568/head
Sebastian Stehle 6 years ago
committed by GitHub
parent
commit
1506f2ba98
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs
  2. 4
      backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs
  3. 4
      backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs
  4. 2
      backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs
  5. 6
      backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidator.cs
  6. 25
      backend/src/Migrations/MigrationPath.cs
  7. 60
      backend/src/Migrations/Migrations/AddPatterns.cs
  8. 30
      backend/src/Migrations/Migrations/ClearRules.cs
  9. 3
      backend/src/Migrations/Migrations/ConvertEventStoreAppId.cs
  10. 10
      backend/src/Migrations/Migrations/CreateAssetSlugs.cs
  11. 118
      backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs
  12. 19
      backend/src/Migrations/Migrations/PopulateGrainIndexes.cs
  13. 28
      backend/src/Migrations/Migrations/RebuildAssetFolders.cs
  14. 11
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppPatterns.cs
  15. 5
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs
  16. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Comments/Comment.cs
  17. 5
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs
  18. 8
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Status.cs
  19. 9
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusTypeConverter.cs
  20. 7
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs
  21. 17
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs
  22. 3
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedAssetEvent.cs
  23. 3
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEvent.cs
  24. 3
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedEvent.cs
  25. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEvent.cs
  26. 3
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEventBase.cs
  27. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/IEnrichedEntityEvent.cs
  28. 8
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs
  29. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs
  30. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs
  31. 10
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs
  32. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs
  33. 4
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs
  34. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs
  35. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs
  36. 15
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  37. 7
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs
  38. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs
  39. 7
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
  40. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs
  41. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/ContentSchemaBuilder.cs
  42. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs
  43. 10
      backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/SchemaBuilder.cs
  44. 13
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventFluidExtensions.cs
  45. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventJintExtension.cs
  46. 3
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs
  47. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/PredefinedPatternsFormatter.cs
  48. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs
  49. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  50. 5
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs
  51. 43
      backend/src/Squidex.Domain.Apps.Core.Operations/IUrlGenerator.cs
  52. 7
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldObject.cs
  53. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/HttpJintExtension.cs
  54. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptOperations.cs
  55. 9
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptVars.cs
  56. 16
      backend/src/Squidex.Domain.Apps.Core.Operations/Tags/ITagService.cs
  57. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/Tags/TagGroups.cs
  58. 5
      backend/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs
  59. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs
  60. 5
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultFieldValueValidatorsFactory.cs
  61. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IAssetInfo.cs
  62. 28
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs
  63. 19
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationContext.cs
  64. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AssetsValidator.cs
  65. 11
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ReferencesValidator.cs
  66. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/UniqueValidator.cs
  67. 31
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs
  68. 30
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderEntity.cs
  69. 17
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs
  70. 17
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs
  71. 43
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  72. 17
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs
  73. 8
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs
  74. 22
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionAll.cs
  75. 16
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionPublished.cs
  76. 44
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs
  77. 10
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  78. 31
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  79. 7
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContent.cs
  80. 18
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContentsByIds.cs
  81. 20
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContentsByQuery.cs
  82. 24
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryIdsAsync.cs
  83. 12
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/StatusSerializer.cs
  84. 3
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/FullText/MongoIndexStorage.cs
  85. 61
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/FullText/MongoTextIndexState.cs
  86. 43
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/FullText/MongoTextIndexerState.cs
  87. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs
  88. 18
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventEntity.cs
  89. 32
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs
  90. 15
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleStatisticsCollection.cs
  91. 15
      backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs
  92. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs
  93. 35
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs
  94. 5
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppExtensions.cs
  95. 3
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs
  96. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppSettingsSearchSource.cs
  97. 13
      backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs
  98. 9
      backend/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs
  99. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs
  100. 8
      backend/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddPattern.cs

3
backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules;
@ -74,7 +73,7 @@ namespace Squidex.Extensions.Actions.Comment
public sealed class CommentJob
{
public NamedId<Guid> AppId { get; set; }
public NamedId<DomainId> AppId { get; set; }
public RefToken Actor { get; set; }

4
backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs

@ -69,13 +69,13 @@ namespace Squidex.Extensions.Actions.Email
return Result.Complete();
}
private async Task CheckConnectionAsync(EmailJob job, CancellationToken ct)
private static async Task CheckConnectionAsync(EmailJob job, CancellationToken ct)
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
var tcs = new TaskCompletionSource<IAsyncResult>();
var state = socket.BeginConnect(job.ServerHost, job.ServerPort, tcs.SetResult, null);
socket.BeginConnect(job.ServerHost, job.ServerPort, tcs.SetResult, null);
using (ct.Register(() =>
{

4
backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs

@ -131,7 +131,7 @@ namespace Squidex.Extensions.Actions.Kafka
}
}
private async Task ProduceAsync<T>(IProducer<string, T> producer, Message<string, T> message, KafkaJob job, CancellationToken ct)
private static async Task ProduceAsync<T>(IProducer<string, T> producer, Message<string, T> message, KafkaJob job, CancellationToken ct)
{
message.Key = job.MessageKey;
@ -181,7 +181,7 @@ namespace Squidex.Extensions.Actions.Kafka
avroProducer?.Dispose();
}
private object GetValue(IJsonValue value, Schema schema)
private static object GetValue(IJsonValue value, Schema schema)
{
switch (value)
{

2
backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs

@ -21,7 +21,7 @@ namespace Squidex.Extensions.Actions.Notification
public sealed class NotificationActionHandler : RuleActionHandler<NotificationAction, NotificationJob>
{
private const string Description = "Send a Notification";
private static readonly NamedId<Guid> NoApp = NamedId.Of(Guid.Empty, "none");
private static readonly NamedId<DomainId> NoApp = NamedId.Of(DomainId.Empty, "none");
private readonly ICommandBus commandBus;
private readonly IUserResolver userResolver;

6
backend/extensions/Squidex.Extensions/Validation/CompositeUniqueValidator.cs

@ -78,15 +78,15 @@ namespace Squidex.Extensions.Validation
{
case BooleanFieldProperties _ when value is JsonBoolean boolean:
return boolean.Value;
case BooleanFieldProperties _ when value is JsonNull _:
case BooleanFieldProperties _ when value is JsonNull:
return ClrValue.Null;
case NumberFieldProperties _ when value is JsonNumber number:
return number.Value;
case NumberFieldProperties _ when value is JsonNull _:
case NumberFieldProperties _ when value is JsonNull:
return ClrValue.Null;
case StringFieldProperties _ when value is JsonString @string:
return @string.Value;
case StringFieldProperties _ when value is JsonNull _:
case StringFieldProperties _ when value is JsonNull:
return ClrValue.Null;
case ReferencesFieldProperties _ when value is JsonArray array && array.FirstOrDefault() is JsonString @string:
return @string.Value;

25
backend/src/Migrations/MigrationPath.cs

@ -18,7 +18,7 @@ namespace Migrations
{
public sealed class MigrationPath : IMigrationPath
{
private const int CurrentVersion = 21;
private const int CurrentVersion = 22;
private readonly IServiceProvider serviceProvider;
public MigrationPath(IServiceProvider serviceProvider)
@ -48,6 +48,12 @@ namespace Migrations
yield return serviceProvider.GetRequiredService<ConvertEventStore>();
}
// Version 22: Also add app id to aggregate id.
if (version < 22)
{
yield return serviceProvider.GetRequiredService<AddAppIdToEventStream>();
}
// Version 07: Introduces AppId for backups.
else if (version < 7)
{
@ -74,14 +80,18 @@ namespace Migrations
}
// Version 14: Schema refactoring
if (version < 14)
// Version 22: Also add app id to aggregate id.
if (version < 22)
{
yield return serviceProvider.GetRequiredService<ClearSchemas>();
yield return serviceProvider.GetRequiredService<ClearRules>();
}
// Version 18: Rebuild assets.
if (version < 18)
// Version 22: Introduce domain id.
if (version < 22)
{
yield return serviceProvider.GetService<RebuildAssetFolders>();
yield return serviceProvider.GetService<RebuildAssets>();
}
else
@ -100,18 +110,13 @@ namespace Migrations
}
// Version 21: Introduce content drafts V2.
if (version < 21)
// Version 22: Introduce domain id.
if (version < 22)
{
yield return serviceProvider.GetRequiredService<RebuildContents>();
}
}
// Version 01: Introduce app patterns.
if (version < 1)
{
yield return serviceProvider.GetRequiredService<AddPatterns>();
}
// Version 13: Json refactoring
if (version < 13)
{

60
backend/src/Migrations/Migrations/AddPatterns.cs

@ -1,60 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Indexes;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Migrations;
namespace Migrations.Migrations
{
public sealed class AddPatterns : IMigration
{
private readonly InitialPatterns initialPatterns;
private readonly ICommandBus commandBus;
private readonly IAppsIndex indexForApps;
public AddPatterns(InitialPatterns initialPatterns, ICommandBus commandBus, IAppsIndex indexForApps)
{
this.indexForApps = indexForApps;
this.initialPatterns = initialPatterns;
this.commandBus = commandBus;
}
public async Task UpdateAsync()
{
var ids = await indexForApps.GetIdsAsync();
foreach (var id in ids)
{
var app = await indexForApps.GetAppAsync(id);
if (app != null && app.Patterns.Count == 0)
{
foreach (var pattern in initialPatterns.Values)
{
var command =
new AddPattern
{
Actor = app.CreatedBy,
AppId = id,
Name = pattern.Name,
PatternId = Guid.NewGuid(),
Pattern = pattern.Pattern,
Message = pattern.Message
};
await commandBus.PublishAsync(command);
}
}
}
}
}
}

30
backend/src/Migrations/Migrations/ClearRules.cs

@ -0,0 +1,30 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.States;
namespace Migrations.Migrations
{
public sealed class ClearRules : IMigration
{
private readonly IStore<Guid> store;
public ClearRules(IStore<Guid> store)
{
this.store = store;
}
public Task UpdateAsync()
{
return store.ClearSnapshotsAsync<Guid, RuleState>();
}
}
}

3
backend/src/Migrations/Migrations/ConvertEventStoreAppId.cs

@ -11,7 +11,6 @@ using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Migrations;
@ -67,7 +66,7 @@ namespace Migrations.Migrations
{
var appId = NamedId<Guid>.Parse(appIdValue.ToString(), Guid.TryParse).Id.ToString();
var eventUpdate = updater.Set($"Events.{index}.Metadata.{SquidexHeaders.AppId}", appId);
var eventUpdate = updater.Set($"Events.{index}.Metadata.AppId", appId);
if (update != null)
{

10
backend/src/Migrations/Migrations/CreateAssetSlugs.cs

@ -5,10 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.States;
@ -16,9 +16,9 @@ namespace Migrations.Migrations
{
public sealed class CreateAssetSlugs : IMigration
{
private readonly ISnapshotStore<AssetState, Guid> stateForAssets;
private readonly ISnapshotStore<AssetState, string> stateForAssets;
public CreateAssetSlugs(ISnapshotStore<AssetState, Guid> stateForAssets)
public CreateAssetSlugs(ISnapshotStore<AssetState, string> stateForAssets)
{
this.stateForAssets = stateForAssets;
}
@ -29,7 +29,9 @@ namespace Migrations.Migrations
{
state.Slug = state.FileName.ToAssetSlug();
await stateForAssets.WriteAsync(state.Id, state, version, version);
var key = DomainId.Combine(state.AppId.Id, state.Id).ToString();
await stateForAssets.WriteAsync(key, state, version, version);
});
}
}

118
backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs

@ -0,0 +1,118 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure.Migrations;
namespace Migrations.Migrations.MongoDb
{
public sealed class AddAppIdToEventStream : IMigration
{
private readonly IMongoDatabase database;
public AddAppIdToEventStream(IMongoDatabase database)
{
this.database = database;
}
public async Task UpdateAsync()
{
var collection = database.GetCollection<BsonDocument>("Events");
const int SizeOfBatch = 200;
const int SizeOfQueue = 20;
var batchBlock = new BatchBlock<BsonDocument>(SizeOfBatch, new GroupingDataflowBlockOptions
{
BoundedCapacity = SizeOfQueue * SizeOfBatch
});
var actionBlock = new ActionBlock<BsonDocument[]>(async batch =>
{
var updates = new List<WriteModel<BsonDocument>>();
foreach (var commit in batch)
{
var eventStream = commit["EventStream"].AsString;
string? appId = null;
foreach (var @event in commit["Events"].AsBsonArray)
{
var metadata = @event["Metadata"].AsBsonDocument;
if (metadata.TryGetValue("AppId", out var value))
{
appId = value.AsString;
}
}
if (appId != null)
{
var parts = eventStream.Split("-");
var domainType = parts[0];
var domainId = string.Join("-", parts.Skip(1));
var newStreamName = $"{domainType}-{appId}--{domainId}";
var update = Builders<BsonDocument>.Update.Set("EventStream", newStreamName);
var i = 0;
foreach (var @event in commit["Events"].AsBsonArray)
{
update = update.Set($"Events.{i}.Metadata.AggregateId", $"{appId}--{domainId}");
update = update.Unset($"Events.{i}.Metadata.AppId");
i++;
}
var filter = Builders<BsonDocument>.Filter.Eq("_id", commit["_id"].AsString);
updates.Add(new UpdateOneModel<BsonDocument>(filter, update));
}
}
if (updates.Count > 0)
{
await collection.BulkWriteAsync(updates);
}
}, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 4,
MaxMessagesPerTask = 1,
BoundedCapacity = SizeOfQueue
});
batchBlock.LinkTo(actionBlock, new DataflowLinkOptions
{
PropagateCompletion = true
});
await collection.Find(new BsonDocument()).ForEachAsync(async commit =>
{
var eventStream = commit["EventStream"].AsString;
if (!eventStream.Contains("--") && !eventStream.StartsWith("app-", StringComparison.OrdinalIgnoreCase))
{
await batchBlock.SendAsync(commit);
}
});
batchBlock.Complete();
await actionBlock.Completion;
}
}
}

19
backend/src/Migrations/Migrations/PopulateGrainIndexes.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Apps.Indexes;
@ -50,20 +49,20 @@ namespace Migrations.Migrations
private async Task RebuildAppIndexes()
{
var appsByName = new Dictionary<string, Guid>();
var appsByUser = new Dictionary<string, HashSet<Guid>>();
var appsByName = new Dictionary<string, DomainId>();
var appsByUser = new Dictionary<string, HashSet<DomainId>>();
bool HasApp(NamedId<Guid> appId, bool consistent, out Guid id)
bool HasApp(NamedId<DomainId> appId, bool consistent, out DomainId id)
{
return appsByName!.TryGetValue(appId.Name, out id) && (!consistent || id == appId.Id);
}
HashSet<Guid> Index(string contributorId)
HashSet<DomainId> Index(string contributorId)
{
return appsByUser!.GetOrAddNew(contributorId);
}
void RemoveApp(NamedId<Guid> appId, bool consistent)
void RemoveApp(NamedId<DomainId> appId, bool consistent)
{
if (HasApp(appId, consistent, out var id))
{
@ -121,9 +120,9 @@ namespace Migrations.Migrations
private async Task RebuildRuleIndexes()
{
var rulesByApp = new Dictionary<Guid, HashSet<Guid>>();
var rulesByApp = new Dictionary<DomainId, HashSet<DomainId>>();
HashSet<Guid> Index(RuleEvent @event)
HashSet<DomainId> Index(RuleEvent @event)
{
return rulesByApp!.GetOrAddNew(@event.AppId.Id);
}
@ -153,9 +152,9 @@ namespace Migrations.Migrations
private async Task RebuildSchemaIndexes()
{
var schemasByApp = new Dictionary<Guid, Dictionary<string, Guid>>();
var schemasByApp = new Dictionary<DomainId, Dictionary<string, DomainId>>();
Dictionary<string, Guid> Index(SchemaEvent @event)
Dictionary<string, DomainId> Index(SchemaEvent @event)
{
return schemasByApp!.GetOrAddNew(@event.AppId.Id);
}

28
backend/src/Migrations/Migrations/RebuildAssetFolders.cs

@ -0,0 +1,28 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Migrations;
namespace Migrations.Migrations
{
public sealed class RebuildAssetFolders : IMigration
{
private readonly Rebuilder rebuilder;
public RebuildAssetFolders(Rebuilder rebuilder)
{
this.rebuilder = rebuilder;
}
public Task UpdateAsync()
{
return rebuilder.RebuildAssetFoldersAsync();
}
}
}

11
backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppPatterns.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
@ -13,7 +12,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class AppPatterns : ImmutableDictionary<Guid, AppPattern>
public sealed class AppPatterns : ImmutableDictionary<DomainId, AppPattern>
{
public static readonly AppPatterns Empty = new AppPatterns();
@ -21,19 +20,19 @@ namespace Squidex.Domain.Apps.Core.Apps
{
}
public AppPatterns(Dictionary<Guid, AppPattern> inner)
public AppPatterns(Dictionary<DomainId, AppPattern> inner)
: base(inner)
{
}
[Pure]
public AppPatterns Remove(Guid id)
public AppPatterns Remove(DomainId id)
{
return Without<AppPatterns>(id);
}
[Pure]
public AppPatterns Add(Guid id, string name, string pattern, string? message = null)
public AppPatterns Add(DomainId id, string name, string pattern, string? message = null)
{
var newPattern = new AppPattern(name, pattern, message);
@ -41,7 +40,7 @@ namespace Squidex.Domain.Apps.Core.Apps
}
[Pure]
public AppPatterns Update(Guid id, string name, string pattern, string? message = null)
public AppPatterns Update(DomainId id, string name, string pattern, string? message = null)
{
Guard.NotNullOrEmpty(name, nameof(name));
Guard.NotNullOrEmpty(pattern, nameof(pattern));

5
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Apps.Json
@ -17,7 +18,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{
protected override void WriteValue(JsonWriter writer, AppPatterns value, JsonSerializer serializer)
{
var json = new Dictionary<Guid, JsonAppPattern>(value.Count);
var json = new Dictionary<DomainId, JsonAppPattern>(value.Count);
foreach (var (key, appPattern) in value)
{
@ -29,7 +30,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
protected override AppPatterns ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<Guid, JsonAppPattern>>(reader)!;
var json = serializer.Deserialize<Dictionary<DomainId, JsonAppPattern>>(reader)!;
return new AppPatterns(json.ToDictionary(x => x.Key, x => x.Value.ToPattern()));
}

4
backend/src/Squidex.Domain.Apps.Core.Model/Comments/Comment.cs

@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Core.Comments
{
public sealed class Comment
{
public Guid Id { get; }
public DomainId Id { get; }
public Instant Time { get; }
@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.Comments
public Uri? Url { get; }
public Comment(Guid id, Instant time, RefToken user, string text, Uri? url = null)
public Comment(DomainId id, Instant time, RefToken user, string text, Uri? url = null)
{
Guard.NotEmpty(id, nameof(id));
Guard.NotNull(user, nameof(user));

5
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Contents.Json
@ -16,7 +17,7 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
{
protected override void WriteValue(JsonWriter writer, Workflows value, JsonSerializer serializer)
{
var json = new Dictionary<Guid, Workflow>(value.Count);
var json = new Dictionary<DomainId, Workflow>(value.Count);
foreach (var (key, workflow) in value)
{
@ -28,7 +29,7 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
protected override Workflows ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<Guid, Workflow>>(reader);
var json = serializer.Deserialize<Dictionary<DomainId, Workflow>>(reader);
return new Workflows(json!);
}

8
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Status.cs

@ -11,8 +11,8 @@ using System.Diagnostics.CodeAnalysis;
namespace Squidex.Domain.Apps.Core.Contents
{
[TypeConverter(typeof(StatusConverter))]
public struct Status : IEquatable<Status>, IComparable<Status>
[TypeConverter(typeof(StatusTypeConverter))]
public readonly struct Status : IEquatable<Status>, IComparable<Status>
{
public static readonly Status Archived = new Status("Archived");
public static readonly Status Draft = new Status("Draft");
@ -37,12 +37,12 @@ namespace Squidex.Domain.Apps.Core.Contents
public bool Equals(Status other)
{
return string.Equals(name, other.name);
return string.Equals(Name, other.Name);
}
public override int GetHashCode()
{
return name?.GetHashCode() ?? 0;
return Name.GetHashCode();
}
public override string ToString()

9
backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusConverter.cs → backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusTypeConverter.cs

@ -11,7 +11,7 @@ using System.Globalization;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class StatusConverter : TypeConverter
public sealed class StatusTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
@ -25,7 +25,12 @@ namespace Squidex.Domain.Apps.Core.Contents
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return new Status(value?.ToString());
if (value is string s)
{
return new Status(s);
}
return default(Status);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)

7
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Squidex.Infrastructure;
@ -20,21 +19,21 @@ namespace Squidex.Domain.Apps.Core.Contents
private const string DefaultName = "Unnamed";
public static readonly IReadOnlyDictionary<Status, WorkflowStep> EmptySteps = new Dictionary<Status, WorkflowStep>();
public static readonly IReadOnlyList<Guid> EmptySchemaIds = new List<Guid>();
public static readonly IReadOnlyList<DomainId> EmptySchemaIds = new List<DomainId>();
public static readonly Workflow Default = CreateDefault();
public static readonly Workflow Empty = new Workflow(default, EmptySteps);
[IgnoreDuringEquals]
public IReadOnlyDictionary<Status, WorkflowStep> Steps { get; } = EmptySteps;
public IReadOnlyList<Guid> SchemaIds { get; } = EmptySchemaIds;
public IReadOnlyList<DomainId> SchemaIds { get; } = EmptySchemaIds;
public Status Initial { get; }
public Workflow(
Status initial,
IReadOnlyDictionary<Status, WorkflowStep>? steps,
IReadOnlyList<Guid>? schemaIds = null,
IReadOnlyList<DomainId>? schemaIds = null,
string? name = null)
: base(name ?? DefaultName)
{

17
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
@ -14,7 +13,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class Workflows : ImmutableDictionary<Guid, Workflow>
public sealed class Workflows : ImmutableDictionary<DomainId, Workflow>
{
public static readonly Workflows Empty = new Workflows();
@ -22,19 +21,19 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public Workflows(Dictionary<Guid, Workflow> inner)
public Workflows(Dictionary<DomainId, Workflow> inner)
: base(inner)
{
}
[Pure]
public Workflows Remove(Guid id)
public Workflows Remove(DomainId id)
{
return Without<Workflows>(id);
}
[Pure]
public Workflows Add(Guid workflowId, string name)
public Workflows Add(DomainId workflowId, string name)
{
Guard.NotNullOrEmpty(name, nameof(name));
@ -46,11 +45,11 @@ namespace Squidex.Domain.Apps.Core.Contents
{
Guard.NotNull(workflow, nameof(workflow));
return With<Workflows>(Guid.Empty, workflow);
return With<Workflows>(default, workflow);
}
[Pure]
public Workflows Set(Guid id, Workflow workflow)
public Workflows Set(DomainId id, Workflow workflow)
{
Guard.NotNull(workflow, nameof(workflow));
@ -58,11 +57,11 @@ namespace Squidex.Domain.Apps.Core.Contents
}
[Pure]
public Workflows Update(Guid id, Workflow workflow)
public Workflows Update(DomainId id, Workflow workflow)
{
Guard.NotNull(workflow, nameof(workflow));
if (id == Guid.Empty)
if (id == DomainId.Empty)
{
return Set(workflow);
}

3
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedAssetEvent.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Infrastructure;
@ -16,7 +15,7 @@ namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
public EnrichedAssetEventType Type { get; set; }
public Guid Id { get; set; }
public DomainId Id { get; set; }
public Instant Created { get; set; }

3
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEvent.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
@ -16,7 +15,7 @@ namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
public EnrichedContentEventType Type { get; set; }
public Guid Id { get; set; }
public DomainId Id { get; set; }
public Instant Created { get; set; }

3
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedEvent.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Infrastructure;
@ -13,7 +12,7 @@ namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
public abstract class EnrichedEvent
{
public NamedId<Guid> AppId { get; set; }
public NamedId<DomainId> AppId { get; set; }
public Instant Timestamp { get; set; }

4
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEvent.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
public EnrichedSchemaEventType Type { get; set; }
public Guid Id
public DomainId Id
{
get { return SchemaId.Id; }
}

3
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEventBase.cs

@ -5,13 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
public abstract class EnrichedSchemaEventBase : EnrichedUserEventBase
{
public NamedId<Guid> SchemaId { get; set; }
public NamedId<DomainId> SchemaId { get; set; }
}
}

4
backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/IEnrichedEntityEvent.cs

@ -5,12 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.EnrichedEvents
{
public interface IEnrichedEntityEvent
{
Guid Id { get; }
DomainId Id { get; }
}
}

8
backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs

@ -5,18 +5,18 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules
{
public sealed class RuleJob
{
public Guid Id { get; set; }
public DomainId Id { get; set; }
public Guid AppId { get; set; }
public DomainId AppId { get; set; }
public Guid RuleId { get; set; }
public DomainId RuleId { get; set; }
public string EventName { get; set; }

4
backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs

@ -5,13 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
{
public sealed class ContentChangedTriggerSchemaV2 : Freezable
{
public Guid SchemaId { get; set; }
public DomainId SchemaId { get; set; }
public string? Condition { get; set; }
}

2
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs

@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
properties.Freeze();
}
private T ValidateProperties(FieldProperties newProperties)
private static T ValidateProperties(FieldProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));

10
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs

@ -5,10 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
{
@ -25,17 +25,17 @@ namespace Squidex.Domain.Apps.Core.Schemas
public ReferencesFieldEditor Editor { get; set; }
public Guid SchemaId
public DomainId SchemaId
{
get
{
return SchemaIds?.FirstOrDefault() ?? Guid.Empty;
return SchemaIds?.FirstOrDefault() ?? default;
}
set
{
if (value != default)
{
SchemaIds = new ReadOnlyCollection<Guid>(new List<Guid> { value });
SchemaIds = new ReadOnlyCollection<DomainId>(new List<DomainId> { value });
}
else
{
@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
}
}
public ReadOnlyCollection<Guid>? SchemaIds { get; set; }
public ReadOnlyCollection<DomainId>? SchemaIds { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

2
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
properties.Freeze();
}
private T ValidateProperties(FieldProperties newProperties)
private static T ValidateProperties(FieldProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));

4
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs

@ -59,9 +59,9 @@ namespace Squidex.Domain.Apps.Core.Schemas
return schema.Properties.Label.WithFallback(schema.Name);
}
public static Guid SingleId(this ReferencesFieldProperties properties)
public static DomainId SingleId(this ReferencesFieldProperties properties)
{
return properties.SchemaIds?.Count == 1 ? properties.SchemaIds[0] : Guid.Empty;
return properties.SchemaIds?.Count == 1 ? properties.SchemaIds[0] : default;
}
public static IEnumerable<RootField> ReferenceFields(this Schema schema)

2
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs

@ -131,7 +131,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{
if (field is IArrayField arrayField)
{
foreach (var (key, value) in data)
foreach (var (_, value) in data)
{
if (value is JsonArray array)
{

8
backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs

@ -11,6 +11,7 @@ using System.Linq;
using System.Text;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
@ -81,7 +82,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
};
}
public static ValueConverter ResolveAssetUrls(IReadOnlyCollection<string>? fields, IUrlGenerator urlGenerator)
public static ValueConverter ResolveAssetUrls(NamedId<DomainId> appId, IReadOnlyCollection<string>? fields, IUrlGenerator urlGenerator)
{
if (fields?.Any() != true)
{
@ -132,10 +133,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{
var id = array[i].ToString();
if (Guid.TryParse(id, out var assetId))
{
array[i] = JsonValue.Create(urlGenerator.AssetContent(assetId));
}
array[i] = JsonValue.Create(urlGenerator.AssetContent(appId, id));
}
}

15
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Text;
using Squidex.Domain.Apps.Core.Contents;
@ -17,18 +16,18 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ContentReferencesExtensions
{
public static HashSet<Guid> GetReferencedIds(this NamedContentData source, Schema schema, int referencesPerField = int.MaxValue)
public static HashSet<DomainId> GetReferencedIds(this NamedContentData source, Schema schema, int referencesPerField = int.MaxValue)
{
Guard.NotNull(schema, nameof(schema));
var extractor = new ReferencesExtractor(new HashSet<Guid>(), referencesPerField);
var extractor = new ReferencesExtractor(new HashSet<DomainId>(), referencesPerField);
AddReferencedIds(source, schema.Fields, extractor);
return extractor.Result;
}
public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet<Guid> result, int referencesPerField = int.MaxValue)
public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{
Guard.NotNull(schema, nameof(schema));
@ -37,7 +36,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
AddReferencedIds(source, schema.Fields, extractor);
}
public static void AddReferencedIds(this NamedContentData source, IEnumerable<IField> fields, HashSet<Guid> result, int referencesPerField = int.MaxValue)
public static void AddReferencedIds(this NamedContentData source, IEnumerable<IField> fields, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{
Guard.NotNull(fields, nameof(fields));
@ -46,7 +45,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
AddReferencedIds(source, fields, extractor);
}
public static void AddReferencedIds(this NamedContentData source, IField field, HashSet<Guid> result, int referencesPerField = int.MaxValue)
public static void AddReferencedIds(this NamedContentData source, IField field, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{
Guard.NotNull(field, nameof(field));
@ -76,9 +75,9 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
}
}
public static HashSet<Guid> GetReferencedIds(this IField field, IJsonValue? value, int referencesPerField = int.MaxValue)
public static HashSet<DomainId> GetReferencedIds(this IField field, IJsonValue? value, int referencesPerField = int.MaxValue)
{
var result = new HashSet<Guid>();
var result = new HashSet<DomainId>();
if (value != null)
{

7
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
@ -15,10 +14,10 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public sealed class ReferencesCleaner : IFieldVisitor<IJsonValue>
{
private readonly HashSet<Guid> validIds;
private readonly HashSet<DomainId> validIds;
private IJsonValue value;
public ReferencesCleaner(HashSet<Guid> validIds)
public ReferencesCleaner(HashSet<DomainId> validIds)
{
Guard.NotNull(validIds, nameof(validIds));
@ -113,7 +112,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
private bool IsValidReference(IJsonValue item)
{
return item is JsonString s && Guid.TryParse(s.Value, out var guid) && validIds.Contains(guid);
return item is JsonString s && validIds.Contains(s.Value);
}
}
}

8
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs

@ -5,15 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ReferencesExtensions
{
public static void AddIds(this IJsonValue? value, HashSet<Guid> result, int take)
public static void AddIds(this IJsonValue? value, HashSet<DomainId> result, int take)
{
var added = 0;
@ -21,9 +21,9 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
foreach (var id in array)
{
if (id.Type == JsonValueType.String && Guid.TryParse(id.ToString(), out var guid))
if (id is JsonString s)
{
result.Add(guid);
result.Add(s.Value);
added++;

7
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
@ -16,16 +15,16 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
internal sealed class ReferencesExtractor : IFieldVisitor<None>
{
private readonly HashSet<Guid> result;
private readonly HashSet<DomainId> result;
private readonly int take;
private IJsonValue? value;
public HashSet<Guid> Result
public HashSet<DomainId> Result
{
get { return result; }
}
public ReferencesExtractor(HashSet<Guid> result, int take)
public ReferencesExtractor(HashSet<DomainId> result, int take)
{
Guard.NotNull(result, nameof(result));

4
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs

@ -5,16 +5,16 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ValueReferencesConverter
{
public static ValueConverter CleanReferences(HashSet<Guid>? validIds = null)
public static ValueConverter CleanReferences(HashSet<DomainId>? validIds = null)
{
if (validIds == null)
{

2
backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/ContentSchemaBuilder.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema
{
Properties =
{
["id"] = SchemaBuilder.GuidProperty($"The id of the {schemaName} content.", true),
["id"] = SchemaBuilder.StringProperty($"The id of the {schemaName} content.", true),
["data"] = SchemaBuilder.ObjectProperty(dataSchema, $"The data of the {schemaName}.", true),
["dataDraft"] = SchemaBuilder.ObjectProperty(dataSchema, $"The draft data of the {schemaName}."),
["version"] = SchemaBuilder.NumberProperty($"The version of the {schemaName}.", true),

4
backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs

@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema
public JsonSchemaProperty? Visit(IField<AssetsFieldProperties> field)
{
var itemSchema = schemaResolver("AssetItem", SchemaBuilder.Guid());
var itemSchema = schemaResolver("AssetItem", SchemaBuilder.String());
return SchemaBuilder.ArrayProperty(itemSchema);
}
@ -113,7 +113,7 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema
public JsonSchemaProperty? Visit(IField<ReferencesFieldProperties> field)
{
var itemSchema = schemaResolver("ReferenceItem", SchemaBuilder.Guid());
var itemSchema = schemaResolver("ReferenceItem", SchemaBuilder.String());
return SchemaBuilder.ArrayProperty(itemSchema);
}

10
backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/SchemaBuilder.cs

@ -16,11 +16,6 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema
return new JsonSchema { Type = JsonObjectType.Object };
}
public static JsonSchema Guid()
{
return new JsonSchema { Type = JsonObjectType.String, Format = JsonFormatStrings.Guid };
}
public static JsonSchema String()
{
return new JsonSchema { Type = JsonObjectType.String };
@ -41,11 +36,6 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema
return Enrich(new JsonSchemaProperty { Type = JsonObjectType.String, Format = JsonFormatStrings.DateTime }, description, isRequired);
}
public static JsonSchemaProperty GuidProperty(string? description = null, bool isRequired = false)
{
return Enrich(new JsonSchemaProperty { Type = JsonObjectType.String, Format = JsonFormatStrings.Guid }, description, isRequired);
}
public static JsonSchemaProperty NumberProperty(string? description = null, bool isRequired = false)
{
return Enrich(new JsonSchemaProperty { Type = JsonObjectType.Number }, description, isRequired);

13
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventFluidExtensions.cs

@ -37,9 +37,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
{
if (context.GetValue("event")?.ToObjectValue() is EnrichedContentEvent contentEvent)
{
if (objectValue.ToObjectValue() is Guid guid && guid != Guid.Empty)
if (objectValue.ToObjectValue() is DomainId id && id != DomainId.Empty)
{
var result = urlGenerator.ContentUI(contentEvent.AppId, contentEvent.SchemaId, guid);
var result = urlGenerator.ContentUI(contentEvent.AppId, contentEvent.SchemaId, id);
return new StringValue(result);
}
@ -53,11 +53,14 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
{
if (input is ObjectValue objectValue)
{
if (objectValue.ToObjectValue() is Guid guid && guid != Guid.Empty)
if (context.GetValue("event")?.ToObjectValue() is EnrichedEvent @event)
{
var result = urlGenerator.AssetContent(guid);
if (objectValue.ToObjectValue() is DomainId id && id != DomainId.Empty)
{
var result = urlGenerator.AssetContent(@event.AppId, id);
return new StringValue(result);
return new StringValue(result);
}
}
}

2
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventJintExtension.cs

@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
{
if (context.TryGetValue("event", out var temp) && temp is EnrichedAssetEvent assetEvent)
{
return urlGenerator.AssetContent(assetEvent.Id);
return urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id);
}
return JsValue.Null;

3
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Core.HandleRules
@ -23,6 +24,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules
bool Trigger(EnrichedEvent @event, RuleTrigger trigger);
bool Trigger(AppEvent @event, RuleTrigger trigger, Guid ruleId);
bool Trigger(AppEvent @event, RuleTrigger trigger, DomainId ruleId);
}
}

2
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/PredefinedPatternsFormatter.cs

@ -118,7 +118,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
{
if (@event is EnrichedAssetEvent assetEvent)
{
return urlGenerator.AssetContent(assetEvent.Id);
return urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id);
}
return null;

4
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs

@ -136,7 +136,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return CombineParts(text, parts);
}
private string CombineParts(string text, List<TextPart> parts)
private static string CombineParts(string text, List<TextPart> parts)
{
var span = text.AsSpan();
@ -224,7 +224,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return default;
}
private (bool IsNew, Match) Match(string test)
private static (bool IsNew, Match) Match(string test)
{
var match = RegexPatternNew.Match(test);

4
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs

@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
this.log = log;
}
public virtual async Task<JobList> CreateJobsAsync(Rule rule, Guid ruleId, Envelope<IEvent> @event, bool ignoreStale = true)
public virtual async Task<JobList> CreateJobsAsync(Rule rule, DomainId ruleId, Envelope<IEvent> @event, bool ignoreStale = true)
{
Guard.NotNull(rule, nameof(rule));
Guard.NotNull(@event, nameof(@event));
@ -139,7 +139,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
var job = new RuleJob
{
Id = Guid.NewGuid(),
Id = DomainId.NewGuid(),
ActionData = string.Empty,
ActionName = actionName,
AppId = enrichedEvent.AppId.Id,

5
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
#pragma warning disable IDE0019 // Use pattern matching
@ -56,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return false;
}
bool IRuleTriggerHandler.Trigger(AppEvent @event, RuleTrigger trigger, Guid ruleId)
bool IRuleTriggerHandler.Trigger(AppEvent @event, RuleTrigger trigger, DomainId ruleId)
{
if (@event is TEvent typed)
{
@ -73,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
protected abstract bool Trigger(TEnrichedEvent @event, TTrigger trigger);
protected virtual bool Trigger(TEvent @event, TTrigger trigger, Guid ruleId)
protected virtual bool Trigger(TEvent @event, TTrigger trigger, DomainId ruleId)
{
return true;
}

43
backend/src/Squidex.Domain.Apps.Core.Operations/IUrlGenerator.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Infrastructure;
@ -15,47 +14,47 @@ namespace Squidex.Domain.Apps.Core
{
bool CanGenerateAssetSourceUrl { get; }
string? AssetSource(Guid assetId, long fileVersion);
string? AssetSource(NamedId<DomainId> appId, DomainId assetId, long fileVersion);
string? AssetThumbnail(Guid assetId, AssetType assetType);
string? AssetThumbnail(NamedId<DomainId> appId, DomainId assetId, AssetType assetType);
string AppSettingsUI(NamedId<Guid> appId);
string AppSettingsUI(NamedId<DomainId> appId);
string AssetsUI(NamedId<Guid> appId);
string AssetsUI(NamedId<DomainId> appId);
string AssetsUI(NamedId<Guid> appId, string? query = null);
string AssetsUI(NamedId<DomainId> appId, string? query = null);
string AssetContent(Guid assetId);
string AssetContent(NamedId<DomainId> appId, DomainId assetId);
string BackupsUI(NamedId<Guid> appId);
string BackupsUI(NamedId<DomainId> appId);
string ClientsUI(NamedId<Guid> appId);
string ClientsUI(NamedId<DomainId> appId);
string ContentsUI(NamedId<Guid> appId);
string ContentsUI(NamedId<DomainId> appId);
string ContentsUI(NamedId<Guid> appId, NamedId<Guid> schemaId);
string ContentsUI(NamedId<DomainId> appId, NamedId<DomainId> schemaId);
string ContentUI(NamedId<Guid> appId, NamedId<Guid> schemaId, Guid contentId);
string ContentUI(NamedId<DomainId> appId, NamedId<DomainId> schemaId, DomainId contentId);
string ContributorsUI(NamedId<Guid> appId);
string ContributorsUI(NamedId<DomainId> appId);
string DashboardUI(NamedId<Guid> appId);
string DashboardUI(NamedId<DomainId> appId);
string LanguagesUI(NamedId<Guid> appId);
string LanguagesUI(NamedId<DomainId> appId);
string PatternsUI(NamedId<Guid> appId);
string PatternsUI(NamedId<DomainId> appId);
string PlansUI(NamedId<Guid> appId);
string PlansUI(NamedId<DomainId> appId);
string RolesUI(NamedId<Guid> appId);
string RolesUI(NamedId<DomainId> appId);
string RulesUI(NamedId<Guid> appId);
string RulesUI(NamedId<DomainId> appId);
string SchemasUI(NamedId<Guid> appId);
string SchemasUI(NamedId<DomainId> appId);
string SchemaUI(NamedId<Guid> appId, NamedId<Guid> schemaId);
string SchemaUI(NamedId<DomainId> appId, NamedId<DomainId> schemaId);
string WorkflowsUI(NamedId<Guid> appId);
string WorkflowsUI(NamedId<DomainId> appId);
string UI();
}

7
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldObject.cs

@ -82,12 +82,9 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper
public override void RemoveOwnProperty(string propertyName)
{
if (valuesToDelete == null)
{
valuesToDelete = new HashSet<string>();
}
valuesToDelete ??= new HashSet<string>();
valuesToDelete.Add(propertyName);
valueProperties?.Remove(propertyName);
MarkChanged();

2
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/HttpJintExtension.cs

@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Core.Scripting.Extensions
return request;
}
private async Task<JsValue> ParseResponse(ExecutionContext context, HttpResponseMessage response)
private static async Task<JsValue> ParseResponse(ExecutionContext context, HttpResponseMessage response)
{
var responseString = await response.Content.ReadAsStringAsync();

8
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptOperations.cs

@ -15,19 +15,19 @@ namespace Squidex.Domain.Apps.Core.Scripting
{
private delegate void MessageDelegate(string? message);
private static readonly MessageDelegate Disallow = new MessageDelegate(message =>
private static readonly MessageDelegate Disallow = message =>
{
message = !string.IsNullOrWhiteSpace(message) ? message : "Not allowed";
throw new DomainForbiddenException(message);
});
};
private static readonly MessageDelegate Reject = new MessageDelegate(message =>
private static readonly MessageDelegate Reject = message =>
{
var errors = !string.IsNullOrWhiteSpace(message) ? new[] { new ValidationError(message) } : null;
throw new ValidationException("Script rejected the operation.", errors);
});
};
public static Engine AddDisallow(this Engine engine)
{

9
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptVars.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Scripting
{
@ -26,15 +27,15 @@ namespace Squidex.Domain.Apps.Core.Scripting
set => SetValue(value);
}
public Guid AppId
public DomainId AppId
{
get => GetValue<Guid>();
get => GetValue<DomainId>();
set => SetValue(value);
}
public Guid ContentId
public DomainId ContentId
{
get => GetValue<Guid>();
get => GetValue<DomainId>();
set => SetValue(value);
}

16
backend/src/Squidex.Domain.Apps.Core.Operations/Tags/ITagService.cs

@ -5,26 +5,26 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Tags
{
public interface ITagService
{
Task<Dictionary<string, string>> GetTagIdsAsync(Guid appId, string group, HashSet<string> names);
Task<Dictionary<string, string>> GetTagIdsAsync(DomainId appId, string group, HashSet<string> names);
Task<Dictionary<string, string>> NormalizeTagsAsync(Guid appId, string group, HashSet<string>? names, HashSet<string>? ids);
Task<Dictionary<string, string>> NormalizeTagsAsync(DomainId appId, string group, HashSet<string>? names, HashSet<string>? ids);
Task<Dictionary<string, string>> DenormalizeTagsAsync(Guid appId, string group, HashSet<string> ids);
Task<Dictionary<string, string>> DenormalizeTagsAsync(DomainId appId, string group, HashSet<string> ids);
Task<TagsSet> GetTagsAsync(Guid appId, string group);
Task<TagsSet> GetTagsAsync(DomainId appId, string group);
Task<TagsExport> GetExportableTagsAsync(Guid appId, string group);
Task<TagsExport> GetExportableTagsAsync(DomainId appId, string group);
Task RebuildTagsAsync(Guid appId, string group, TagsExport tags);
Task RebuildTagsAsync(DomainId appId, string group, TagsExport tags);
Task ClearAsync(Guid appId, string group);
Task ClearAsync(DomainId appId, string group);
}
}

4
backend/src/Squidex.Domain.Apps.Core.Operations/Tags/TagGroups.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Tags
{
@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Core.Tags
{
public const string Assets = "Assets";
public static string Schemas(Guid schemaId)
public static string Schemas(DomainId schemaId)
{
return $"Schemas_{schemaId}";
}

5
backend/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
@ -17,7 +16,7 @@ namespace Squidex.Domain.Apps.Core.Tags
{
public static class TagNormalizer
{
public static async Task NormalizeAsync(this ITagService tagService, Guid appId, Guid schemaId, Schema schema, NamedContentData newData, NamedContentData? oldData)
public static async Task NormalizeAsync(this ITagService tagService, DomainId appId, DomainId schemaId, Schema schema, NamedContentData newData, NamedContentData? oldData)
{
Guard.NotNull(tagService, nameof(tagService));
Guard.NotNull(schema, nameof(schema));
@ -53,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Tags
}
}
public static async Task DenormalizeAsync(this ITagService tagService, Guid appId, Guid schemaId, Schema schema, params NamedContentData[] datas)
public static async Task DenormalizeAsync(this ITagService tagService, DomainId appId, DomainId schemaId, Schema schema, params NamedContentData[] datas)
{
Guard.NotNull(tagService, nameof(tagService));
Guard.NotNull(schema, nameof(schema));

1
backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs

@ -45,6 +45,7 @@ namespace Squidex.Domain.Apps.Core.Templates
FluidValue.SetTypeMapping(type, x => new StringValue(x.ToString()));
}
globalTypes.Register<NamedId<DomainId>>();
globalTypes.Register<NamedId<Guid>>();
globalTypes.Register<NamedId<string>>();
globalTypes.Register<NamedId<long>>();

5
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/DefaultFieldValueValidatorsFactory.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using NodaTime;
using Squidex.Domain.Apps.Core.Schemas;
@ -59,7 +58,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
if (!field.Properties.AllowDuplicates)
{
yield return new UniqueValuesValidator<Guid>();
yield return new UniqueValuesValidator<string>();
}
}
@ -127,7 +126,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
if (!field.Properties.AllowDuplicates)
{
yield return new UniqueValuesValidator<Guid>();
yield return new UniqueValuesValidator<string>();
}
}

4
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/IAssetInfo.cs

@ -5,14 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ValidateContent
{
public interface IAssetInfo
{
Guid AssetId { get; }
DomainId AssetId { get; }
long FileSize { get; }

28
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs

@ -35,12 +35,12 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
public (object? Result, JsonError? Error) Visit(IField<AssetsFieldProperties> field)
{
return ConvertToGuidList();
return ConvertToStringList();
}
public (object? Result, JsonError? Error) Visit(IField<ReferencesFieldProperties> field)
{
return ConvertToGuidList();
return ConvertToStringList();
}
public (object? Result, JsonError? Error) Visit(IField<TagsFieldProperties> field)
@ -152,30 +152,6 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
return (value, null);
}
private (object? Result, JsonError? Error) ConvertToGuidList()
{
if (value is JsonArray array)
{
var result = new List<Guid>(array.Count);
foreach (var item in array)
{
if (item is JsonString s && Guid.TryParse(s.Value, out var guid))
{
result.Add(guid);
}
else
{
return (null, new JsonError("Invalid json type, expected array of guid strings."));
}
}
return (result, null);
}
return (null, new JsonError("Invalid json type, expected array of guid strings."));
}
private (object? Result, JsonError? Error) ConvertToStringList()
{
if (value is JsonArray array)

19
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationContext.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Immutable;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
@ -16,33 +15,33 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
{
public ImmutableQueue<string> Path { get; }
public NamedId<Guid> AppId { get; }
public NamedId<DomainId> AppId { get; }
public NamedId<Guid> SchemaId { get; }
public NamedId<DomainId> SchemaId { get; }
public Schema Schema { get; }
public Guid ContentId { get; }
public DomainId ContentId { get; }
public bool IsOptional { get; }
public ValidationMode Mode { get; }
public ValidationContext(
NamedId<Guid> appId,
NamedId<Guid> schemaId,
NamedId<DomainId> appId,
NamedId<DomainId> schemaId,
Schema schema,
Guid contentId,
DomainId contentId,
ValidationMode mode = ValidationMode.Default)
: this(appId, schemaId, schema, contentId, ImmutableQueue<string>.Empty, false, mode)
{
}
private ValidationContext(
NamedId<Guid> appId,
NamedId<Guid> schemaId,
NamedId<DomainId> appId,
NamedId<DomainId> schemaId,
Schema schema,
Guid contentId,
DomainId contentId,
ImmutableQueue<string> path,
bool isOptional,
ValidationMode mode = ValidationMode.Default)

4
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/AssetsValidator.cs

@ -15,7 +15,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
public delegate Task<IReadOnlyList<IAssetInfo>> CheckAssets(IEnumerable<Guid> ids);
public delegate Task<IReadOnlyList<IAssetInfo>> CheckAssets(IEnumerable<DomainId> ids);
public sealed class AssetsValidator : IValidator
{
@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
return;
}
if (value is ICollection<Guid> assetIds && assetIds.Count > 0)
if (value is ICollection<DomainId> assetIds && assetIds.Count > 0)
{
var assets = await checkAssets(assetIds);
var index = 0;

11
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ReferencesValidator.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -13,14 +12,14 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
public delegate Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> CheckContentsByIds(HashSet<Guid> ids);
public delegate Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> CheckContentsByIds(HashSet<DomainId> ids);
public sealed class ReferencesValidator : IValidator
{
private readonly IEnumerable<Guid>? schemaIds;
private readonly IEnumerable<DomainId>? schemaIds;
private readonly CheckContentsByIds checkReferences;
public ReferencesValidator(IEnumerable<Guid>? schemaIds, CheckContentsByIds checkReferences)
public ReferencesValidator(IEnumerable<DomainId>? schemaIds, CheckContentsByIds checkReferences)
{
Guard.NotNull(checkReferences, nameof(checkReferences));
@ -36,7 +35,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
return;
}
if (value is ICollection<Guid> contentIds)
if (value is ICollection<DomainId> contentIds)
{
var foundIds = await checkReferences(contentIds.ToHashSet());
@ -44,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
var (schemaId, _) = foundIds.FirstOrDefault(x => x.Id == id);
if (schemaId == Guid.Empty)
if (schemaId == DomainId.Empty)
{
addError(context.Path, $"Contains invalid reference '{id}'.");
}

4
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/UniqueValidator.cs

@ -5,15 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Core.ValidateContent.Validators
{
public delegate Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> CheckUniqueness(FilterNode<ClrValue> filter);
public delegate Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> CheckUniqueness(FilterNode<ClrValue> filter);
public sealed class UniqueValidator : IValidator
{

31
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs

@ -5,9 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Core.Assets;
@ -17,21 +15,27 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public sealed class MongoAssetEntity : IAssetEntity, IVersionedEntity<Guid>
public sealed class MongoAssetEntity : IAssetEntity, IVersionedEntity<DomainId>
{
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
public DomainId DocumentId { get; set; }
[BsonRequired]
[BsonElement("_ai")]
[BsonRepresentation(BsonType.String)]
public Guid IndexedAppId { get; set; }
public DomainId IndexedAppId { get; set; }
[BsonIgnoreIfDefault]
[BsonElement("id")]
public DomainId Id { get; set; }
[BsonIgnoreIfDefault]
[BsonElement("pi")]
public Guid ParentId { get; set; }
public DomainId ParentId { get; set; }
[BsonRequired]
[BsonElement("ai")]
public NamedId<DomainId> AppId { get; set; }
[BsonRequired]
[BsonElement("ct")]
@ -41,10 +45,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
[BsonElement("mt")]
public Instant LastModified { get; set; }
[BsonRequired]
[BsonElement("ai")]
public NamedId<Guid> AppId { get; set; }
[BsonRequired]
[BsonElement("mm")]
public string MimeType { get; set; }
@ -102,9 +102,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
[BsonElement("md")]
public AssetMetadata Metadata { get; set; }
public Guid AssetId
public DomainId AssetId
{
get { return Id; }
}
public DomainId UniqueId
{
get { return DocumentId; }
}
}
}

30
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderEntity.cs

@ -5,30 +5,35 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public sealed class MongoAssetFolderEntity : IAssetFolderEntity
public sealed class MongoAssetFolderEntity : IAssetFolderEntity, IVersionedEntity<DomainId>
{
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
public DomainId DocumentId { get; set; }
[BsonRequired]
[BsonElement("_ai")]
[BsonRepresentation(BsonType.String)]
public Guid IndexedAppId { get; set; }
public DomainId IndexedAppId { get; set; }
[BsonRequired]
[BsonElement("id")]
public DomainId Id { get; set; }
[BsonRequired]
[BsonElement("pi")]
public Guid ParentId { get; set; }
public DomainId ParentId { get; set; }
[BsonRequired]
[BsonElement("ai")]
public NamedId<DomainId> AppId { get; set; }
[BsonRequired]
[BsonElement("ct")]
@ -38,10 +43,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
[BsonElement("mt")]
public Instant LastModified { get; set; }
[BsonRequired]
[BsonElement("ai")]
public NamedId<Guid> AppId { get; set; }
[BsonRequired]
[BsonElement("fn")]
public string FolderName { get; set; }
@ -61,5 +62,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
[BsonRequired]
[BsonElement("dl")]
public bool IsDeleted { get; set; }
public DomainId UniqueId
{
get { return DocumentId; }
}
}
}

17
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs

@ -46,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}, ct);
}
public async Task<IResultList<IAssetFolderEntity>> QueryAsync(Guid appId, Guid parentId)
public async Task<IResultList<IAssetFolderEntity>> QueryAsync(DomainId appId, DomainId parentId)
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>("QueryAsyncByQuery"))
{
@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IReadOnlyList<Guid>> QueryChildIdsAsync(Guid appId, Guid parentId)
public async Task<IReadOnlyList<DomainId>> QueryChildIdsAsync(DomainId appId, DomainId parentId)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
@ -67,23 +67,20 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).Only(x => x.Id)
.ToListAsync();
return assetFolderEntities.Select(x => Guid.Parse(x[IdField.Value].AsString)).ToList();
return assetFolderEntities.Select(x => DomainId.Create(x[IdField.Value].AsString)).ToList();
}
}
public async Task<IAssetFolderEntity?> FindAssetFolderAsync(Guid id)
public async Task<IAssetFolderEntity?> FindAssetFolderAsync(DomainId appId, DomainId id)
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>())
{
var documentId = DomainId.Combine(appId, id).ToString();
var assetFolderEntity =
await Collection.Find(x => x.Id == id)
await Collection.Find(x => x.DocumentId == documentId && !x.IsDeleted)
.FirstOrDefaultAsync();
if (assetFolderEntity?.IsDeleted == true)
{
return null;
}
return assetFolderEntity;
}
}

17
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs

@ -19,14 +19,14 @@ using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public sealed partial class MongoAssetFolderRepository : ISnapshotStore<AssetFolderState, Guid>
public sealed partial class MongoAssetFolderRepository : ISnapshotStore<AssetFolderState, DomainId>
{
async Task<(AssetFolderState Value, long Version)> ISnapshotStore<AssetFolderState, Guid>.ReadAsync(Guid key)
async Task<(AssetFolderState Value, long Version)> ISnapshotStore<AssetFolderState, DomainId>.ReadAsync(DomainId key)
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>())
{
var existing =
await Collection.Find(x => x.Id == key)
await Collection.Find(x => x.DocumentId == key)
.FirstOrDefaultAsync();
if (existing != null)
@ -38,20 +38,19 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
async Task ISnapshotStore<AssetFolderState, Guid>.WriteAsync(Guid key, AssetFolderState value, long oldVersion, long newVersion)
async Task ISnapshotStore<AssetFolderState, DomainId>.WriteAsync(DomainId key, AssetFolderState value, long oldVersion, long newVersion)
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>())
{
var entity = SimpleMapper.Map(value, new MongoAssetFolderEntity());
entity.Version = newVersion;
entity.IndexedAppId = value.AppId.Id;
await Collection.ReplaceOneAsync(x => x.Id == key && x.Version == oldVersion, entity, UpsertReplace);
await Collection.UpsertVersionedAsync(key, oldVersion, newVersion, entity);
}
}
async Task ISnapshotStore<AssetFolderState, Guid>.ReadAllAsync(Func<AssetFolderState, long, Task> callback, CancellationToken ct)
async Task ISnapshotStore<AssetFolderState, DomainId>.ReadAllAsync(Func<AssetFolderState, long, Task> callback, CancellationToken ct)
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>())
{
@ -59,11 +58,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
async Task ISnapshotStore<AssetFolderState, Guid>.RemoveAsync(Guid key)
async Task ISnapshotStore<AssetFolderState, DomainId>.RemoveAsync(DomainId key)
{
using (Profiler.TraceMethod<MongoAssetFolderRepository>())
{
await Collection.DeleteOneAsync(x => x.Id == key);
await Collection.DeleteOneAsync(x => x.DocumentId == key);
}
}

43
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}, ct);
}
public async Task<IResultList<IAssetEntity>> QueryAsync(Guid appId, Guid? parentId, ClrQuery query)
public async Task<IResultList<IAssetEntity>> QueryAsync(DomainId appId, DomainId? parentId, ClrQuery query)
{
using (Profiler.TraceMethod<MongoAssetRepository>("QueryAsyncByQuery"))
{
@ -96,31 +96,31 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IReadOnlyList<Guid>> QueryIdsAsync(Guid appId, HashSet<Guid> ids)
public async Task<IReadOnlyList<DomainId>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids)
{
using (Profiler.TraceMethod<MongoAssetRepository>("QueryAsyncByIds"))
{
var assetEntities =
await Collection.Find(BuildFilter(appId, ids)).Only(x => x.Id)
await Collection.Find(BuildFilter(appId, ids)).Only(x => x.DocumentId)
.ToListAsync();
return assetEntities.Select(x => Guid.Parse(x[IdField.Value].AsString)).ToList();
return assetEntities.Select(x => DomainId.Create(x[IdField.Value].AsString)).ToList();
}
}
public async Task<IReadOnlyList<Guid>> QueryChildIdsAsync(Guid appId, Guid parentId)
public async Task<IReadOnlyList<DomainId>> QueryChildIdsAsync(DomainId appId, DomainId parentId)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
var assetEntities =
await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).Only(x => x.Id)
await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).Only(x => x.DocumentId)
.ToListAsync();
return assetEntities.Select(x => Guid.Parse(x[IdField.Value].AsString)).ToList();
return assetEntities.Select(x => DomainId.Create(x[IdField.Value].AsString)).ToList();
}
}
public async Task<IResultList<IAssetEntity>> QueryAsync(Guid appId, HashSet<Guid> ids)
public async Task<IResultList<IAssetEntity>> QueryAsync(DomainId appId, HashSet<DomainId> ids)
{
using (Profiler.TraceMethod<MongoAssetRepository>("QueryAsyncByIds"))
{
@ -132,7 +132,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IAssetEntity?> FindAssetBySlugAsync(Guid appId, string slug)
public async Task<IAssetEntity?> FindAssetBySlugAsync(DomainId appId, string slug)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
@ -144,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IReadOnlyList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash)
public async Task<IReadOnlyList<IAssetEntity>> QueryByHashAsync(DomainId appId, string hash)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
@ -156,7 +156,21 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IAssetEntity?> FindAssetAsync(Guid id)
public async Task<IAssetEntity?> FindAssetAsync(DomainId appId, DomainId id)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
var documentId = DomainId.Combine(appId, id).ToString();
var assetEntity =
await Collection.Find(x => x.DocumentId == documentId && !x.IsDeleted)
.FirstOrDefaultAsync();
return assetEntity;
}
}
public async Task<IAssetEntity?> FindAssetAsync(DomainId id)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
@ -168,11 +182,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
private static FilterDefinition<MongoAssetEntity> BuildFilter(Guid appId, HashSet<Guid> ids)
private static FilterDefinition<MongoAssetEntity> BuildFilter(DomainId appId, HashSet<DomainId> ids)
{
var documentIds = ids.Select(x => DomainId.Combine(appId, x));
return Filter.And(
Filter.Eq(x => x.IndexedAppId, appId),
Filter.In(x => x.Id, ids),
Filter.In(x => x.Id, documentIds),
Filter.Ne(x => x.IsDeleted, true));
}

17
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs

@ -19,14 +19,14 @@ using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public sealed partial class MongoAssetRepository : ISnapshotStore<AssetState, Guid>
public sealed partial class MongoAssetRepository : ISnapshotStore<AssetState, DomainId>
{
async Task<(AssetState Value, long Version)> ISnapshotStore<AssetState, Guid>.ReadAsync(Guid key)
async Task<(AssetState Value, long Version)> ISnapshotStore<AssetState, DomainId>.ReadAsync(DomainId key)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
var existing =
await Collection.Find(x => x.Id == key)
await Collection.Find(x => x.DocumentId == key)
.FirstOrDefaultAsync();
if (existing != null)
@ -38,20 +38,19 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
async Task ISnapshotStore<AssetState, Guid>.WriteAsync(Guid key, AssetState value, long oldVersion, long newVersion)
async Task ISnapshotStore<AssetState, DomainId>.WriteAsync(DomainId key, AssetState value, long oldVersion, long newVersion)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
var entity = SimpleMapper.Map(value, new MongoAssetEntity());
entity.Version = newVersion;
entity.IndexedAppId = value.AppId.Id;
await Collection.UpsertVersionedAsync(key, oldVersion, entity);
await Collection.UpsertVersionedAsync(key, oldVersion, newVersion, entity);
}
}
async Task ISnapshotStore<AssetState, Guid>.ReadAllAsync(Func<AssetState, long, Task> callback, CancellationToken ct)
async Task ISnapshotStore<AssetState, DomainId>.ReadAllAsync(Func<AssetState, long, Task> callback, CancellationToken ct)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
@ -59,11 +58,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
async Task ISnapshotStore<AssetState, Guid>.RemoveAsync(Guid key)
async Task ISnapshotStore<AssetState, DomainId>.RemoveAsync(DomainId key)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{
await Collection.DeleteOneAsync(x => x.Id == key);
await Collection.DeleteOneAsync(x => x.DocumentId == key);
}
}

8
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs

@ -5,11 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors
return query;
}
public static FilterDefinition<MongoAssetEntity> BuildFilter(this ClrQuery query, Guid appId, Guid? parentId)
public static FilterDefinition<MongoAssetEntity> BuildFilter(this ClrQuery query, DomainId appId, DomainId? parentId)
{
var filters = new List<FilterDefinition<MongoAssetEntity>>
{
@ -46,12 +46,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors
if (parentId.HasValue)
{
if (parentId == Guid.Empty)
if (parentId == DomainId.Empty)
{
filters.Add(
Filter.Or(
Filter.Exists(x => x.ParentId, false),
Filter.Eq(x => x.ParentId, Guid.Empty)));
Filter.Eq(x => x.ParentId, DomainId.Empty)));
}
else
{

22
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionAll.cs

@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<Guid> ids)
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<DomainId> ids)
{
Guard.NotNull(app, nameof(app));
@ -81,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<Guid> ids)
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<DomainId> ids)
{
Guard.NotNull(app, nameof(app));
@ -93,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IContentEntity?> FindContentAsync(ISchemaEntity schema, Guid id)
public async Task<IContentEntity?> FindContentAsync(ISchemaEntity schema, DomainId id)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -109,7 +109,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> QueryIdsAsync(Guid appId, HashSet<Guid> ids)
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -117,7 +117,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> QueryIdsAsync(Guid appId, Guid schemaId, FilterNode<ClrValue> filterNode)
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> QueryIdsAsync(DomainId appId, DomainId schemaId, FilterNode<ClrValue> filterNode)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -125,19 +125,19 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public Task<MongoContentEntity> FindAsync(Guid id)
public Task<MongoContentEntity> FindAsync(DomainId documentId)
{
return Collection.Find(x => x.Id == id).FirstOrDefaultAsync();
return Collection.Find(x => x.DocumentId == documentId).FirstOrDefaultAsync();
}
public Task UpsertVersionedAsync(Guid id, long oldVersion, MongoContentEntity entity)
public Task UpsertVersionedAsync(DomainId documentId, long oldVersion, MongoContentEntity entity)
{
return Collection.UpsertVersionedAsync(id, oldVersion, entity);
return Collection.UpsertVersionedAsync(documentId, oldVersion, entity.Version, entity);
}
public Task RemoveAsync(Guid id)
public Task RemoveAsync(DomainId documentId)
{
return Collection.DeleteOneAsync(x => x.Id == id);
return Collection.DeleteOneAsync(x => x.DocumentId == documentId);
}
}
}

16
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollectionPublished.cs

@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<Guid> ids)
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<DomainId> ids)
{
Guard.NotNull(app, nameof(app));
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<Guid> ids)
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<DomainId> ids)
{
Guard.NotNull(app, nameof(app));
@ -97,7 +97,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IContentEntity?> FindContentAsync(ISchemaEntity schema, Guid id)
public async Task<IContentEntity?> FindContentAsync(ISchemaEntity schema, DomainId id)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public async Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> QueryIdsAsync(Guid appId, HashSet<Guid> ids)
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -113,14 +113,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public Task UpsertVersionedAsync(Guid id, long oldVersion, MongoContentEntity entity)
public Task UpsertVersionedAsync(DomainId documentId, long oldVersion, MongoContentEntity entity)
{
return Collection.UpsertVersionedAsync(id, oldVersion, entity);
return Collection.UpsertVersionedAsync(documentId, oldVersion, entity.Version, entity);
}
public Task RemoveAsync(Guid id)
public Task RemoveAsync(DomainId documentId)
{
return Collection.DeleteOneAsync(x => x.Id == id);
return Collection.DeleteOneAsync(x => x.DocumentId == documentId);
}
}
}

44
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs

@ -5,9 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using MongoDB.Bson;
using System.Linq;
using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
@ -21,29 +20,37 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
[BsonIgnoreExtraElements]
public sealed class MongoContentEntity : IContentEntity, IVersionedEntity<Guid>
public sealed class MongoContentEntity : IContentEntity, IVersionedEntity<DomainId>
{
private NamedContentData data;
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
public DomainId DocumentId { get; set; }
[BsonRequired]
[BsonElement("_ai")]
[BsonRepresentation(BsonType.String)]
public Guid IndexedAppId { get; set; }
public DomainId IndexedAppId { get; set; }
[BsonRequired]
[BsonElement("_si")]
[BsonRepresentation(BsonType.String)]
public Guid IndexedSchemaId { get; set; }
public DomainId IndexedSchemaId { get; set; }
[BsonRequired]
[BsonElement("ai")]
public NamedId<DomainId> AppId { get; set; }
[BsonRequired]
[BsonElement("si")]
public NamedId<DomainId> SchemaId { get; set; }
[BsonRequired]
[BsonElement("rf")]
[BsonRepresentation(BsonType.String)]
public HashSet<Guid>? ReferencedIds { get; set; }
public HashSet<DomainId>? ReferencedIds { get; set; }
[BsonRequired]
[BsonElement("id")]
public DomainId Id { get; set; }
[BsonRequired]
[BsonElement("ss")]
@ -58,14 +65,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonJson]
public IdContentData DataByIds { get; set; }
[BsonRequired]
[BsonElement("ai")]
public NamedId<Guid> AppId { get; set; }
[BsonRequired]
[BsonElement("si")]
public NamedId<Guid> SchemaId { get; set; }
[BsonIgnoreIfNull]
[BsonElement("sa")]
public Instant? ScheduledAt { get; set; }
@ -104,9 +103,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
get { return data; }
}
public DomainId UniqueId
{
get { return DocumentId; }
}
public void LoadData(NamedContentData data, Schema schema, DataConverter converter)
{
ReferencedIds = data.GetReferencedIds(schema);
ReferencedIds = data.GetReferencedIds(schema).Select(x => DomainId.Combine(AppId, x)).ToHashSet();
DataByIds = converter.ToMongoModel(data, schema);
}

10
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -66,7 +66,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<Guid> ids, SearchScope scope)
public Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, HashSet<DomainId> ids, SearchScope scope)
{
if (scope == SearchScope.All)
{
@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<Guid> ids, SearchScope scope)
public Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryAsync(IAppEntity app, HashSet<DomainId> ids, SearchScope scope)
{
if (scope == SearchScope.All)
{
@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public Task<IContentEntity?> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, SearchScope scope)
public Task<IContentEntity?> FindContentAsync(IAppEntity app, ISchemaEntity schema, DomainId id, SearchScope scope)
{
if (scope == SearchScope.All)
{
@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> QueryIdsAsync(Guid appId, HashSet<Guid> ids, SearchScope scope)
public Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> QueryIdsAsync(DomainId appId, HashSet<DomainId> ids, SearchScope scope)
{
if (scope == SearchScope.All)
{
@ -119,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return collectionAll.QueryScheduledWithoutDataAsync(now, callback);
}
public Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> QueryIdsAsync(Guid appId, Guid schemaId, FilterNode<ClrValue> filterNode)
public Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> QueryIdsAsync(DomainId appId, DomainId schemaId, FilterNode<ClrValue> filterNode)
{
return collectionAll.QueryIdsAsync(appId, schemaId, filterNode);
}

31
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -18,14 +18,14 @@ using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public partial class MongoContentRepository : ISnapshotStore<ContentState, Guid>
public partial class MongoContentRepository : ISnapshotStore<ContentState, DomainId>
{
Task ISnapshotStore<ContentState, Guid>.ReadAllAsync(Func<ContentState, long, Task> callback, CancellationToken ct)
Task ISnapshotStore<ContentState, DomainId>.ReadAllAsync(Func<ContentState, long, Task> callback, CancellationToken ct)
{
throw new NotSupportedException();
}
async Task ISnapshotStore<ContentState, Guid>.ClearAsync()
async Task ISnapshotStore<ContentState, DomainId>.ClearAsync()
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
async Task ISnapshotStore<ContentState, Guid>.RemoveAsync(Guid key)
async Task ISnapshotStore<ContentState, DomainId>.RemoveAsync(DomainId key)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
async Task<(ContentState Value, long Version)> ISnapshotStore<ContentState, Guid>.ReadAsync(Guid key)
async Task<(ContentState Value, long Version)> ISnapshotStore<ContentState, DomainId>.ReadAsync(DomainId key)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
@ -62,11 +62,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
async Task ISnapshotStore<ContentState, Guid>.WriteAsync(Guid key, ContentState value, long oldVersion, long newVersion)
async Task ISnapshotStore<ContentState, DomainId>.WriteAsync(DomainId key, ContentState value, long oldVersion, long newVersion)
{
using (Profiler.TraceMethod<MongoContentRepository>())
{
if (value.SchemaId.Id == Guid.Empty)
if (value.SchemaId.Id == DomainId.Empty)
{
return;
}
@ -87,13 +87,15 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
else
{
await DeletePublishedContentAsync(value.Id);
await DeletePublishedContentAsync(value.AppId.Id, value.Id);
}
}
private Task DeletePublishedContentAsync(Guid key)
private Task DeletePublishedContentAsync(DomainId appId, DomainId id)
{
return collectionPublished.RemoveAsync(key);
var documentId = DomainId.Combine(appId, id).ToString();
return collectionPublished.RemoveAsync(documentId);
}
private async Task UpsertDraftContentAsync(ContentState value, long oldVersion, long newVersion, ISchemaEntity schema)
@ -105,34 +107,37 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
Version = newVersion
});
content.DocumentId = value.UniqueId;
content.ScheduledAt = value.ScheduleJob?.DueTime;
content.ScheduleJob = value.ScheduleJob;
content.NewStatus = value.NewStatus;
content.LoadData(value.Data, schema.SchemaDef, converter);
await collectionAll.UpsertVersionedAsync(content.Id, oldVersion, content);
await collectionAll.UpsertVersionedAsync(content.DocumentId, oldVersion, content);
}
private async Task UpsertPublishedContentAsync(ContentState value, long oldVersion, long newVersion, ISchemaEntity schema)
{
var content = SimpleMapper.Map(value, new MongoContentEntity
{
Id = value.Id,
IndexedAppId = value.AppId.Id,
IndexedSchemaId = value.SchemaId.Id,
Version = newVersion
});
content.DocumentId = value.UniqueId;
content.ScheduledAt = null;
content.ScheduleJob = null;
content.NewStatus = null;
content.LoadData(value.CurrentVersion.Data, schema.SchemaDef, converter);
await collectionPublished.UpsertVersionedAsync(content.Id, oldVersion, content);
await collectionPublished.UpsertVersionedAsync(content.DocumentId, oldVersion, content);
}
private async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid schemaId)
private async Task<ISchemaEntity> GetSchemaAsync(DomainId appId, DomainId schemaId)
{
var schema = await appProvider.GetSchemaAsync(appId, schemaId, true);

7
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContent.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Contents;
@ -23,11 +22,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
this.converter = converter;
}
public async Task<IContentEntity?> DoAsync(ISchemaEntity schema, Guid id)
public async Task<IContentEntity?> DoAsync(ISchemaEntity schema, DomainId id)
{
Guard.NotNull(schema, nameof(schema));
var find = Collection.Find(x => x.Id == id);
var documentId = DomainId.Combine(schema.AppId, id).ToString();
var find = Collection.Find(x => x.DocumentId == documentId);
var contentEntity = await find.FirstOrDefaultAsync();

18
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContentsByIds.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -28,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
this.appProvider = appProvider;
}
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> DoAsync(Guid appId, ISchemaEntity? schema, HashSet<Guid> ids)
public async Task<List<(IContentEntity Content, ISchemaEntity Schema)>> DoAsync(DomainId appId, ISchemaEntity? schema, HashSet<DomainId> ids)
{
Guard.NotNull(ids, nameof(ids));
@ -52,9 +51,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return result;
}
private async Task<IDictionary<Guid, ISchemaEntity>> GetSchemasAsync(Guid appId, ISchemaEntity? schema, List<MongoContentEntity> contentItems)
private async Task<IDictionary<DomainId, ISchemaEntity>> GetSchemasAsync(DomainId appId, ISchemaEntity? schema, List<MongoContentEntity> contentItems)
{
var schemas = new Dictionary<Guid, ISchemaEntity>();
var schemas = new Dictionary<DomainId, ISchemaEntity>();
if (schema != null)
{
@ -79,29 +78,30 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return schemas;
}
private static FilterDefinition<MongoContentEntity> CreateFilter(Guid appId, ICollection<Guid> ids)
private static FilterDefinition<MongoContentEntity> CreateFilter(DomainId appId, ICollection<DomainId> ids)
{
var filters = new List<FilterDefinition<MongoContentEntity>>
{
Filter.Eq(x => x.IndexedAppId, appId),
Filter.Ne(x => x.IsDeleted, true)
};
if (ids != null && ids.Count > 0)
{
var documentIds = ids.Select(x => DomainId.Combine(appId, x)).ToList();
if (ids.Count > 1)
{
filters.Add(
Filter.Or(
Filter.In(x => x.Id, ids)));
Filter.In(x => x.DocumentId, documentIds)));
}
else
{
var first = ids.First();
var first = documentIds.First();
filters.Add(
Filter.Or(
Filter.Eq(x => x.Id, first)));
Filter.Eq(x => x.DocumentId, first)));
}
}

20
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryContentsByQuery.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@ -34,8 +33,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
public DomainId Id { get; set; }
public MongoContentEntity[] Joined { get; set; }
}
@ -51,6 +49,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
var index =
new CreateIndexModel<MongoContentEntity>(Index
.Ascending(x => x.IndexedAppId)
.Ascending(x => x.IndexedSchemaId)
.Ascending(x => x.IsDeleted)
.Ascending(x => x.ReferencedIds)
@ -69,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
query = query.AdjustToModel(schema.SchemaDef);
List<Guid>? fullTextIds = null;
List<DomainId>? fullTextIds = null;
if (!string.IsNullOrWhiteSpace(query.FullText))
{
@ -83,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
}
}
var filter = CreateFilter(schema.Id, fullTextIds, query);
var filter = CreateFilter(schema.AppId.Id, schema.Id, fullTextIds, query);
var contentCount = Collection.Find(filter).CountDocumentsAsync();
var contentItems = FindContentsAsync(query, filter);
@ -125,7 +124,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
.QuerySort(query)
.QuerySkip(query)
.QueryLimit(query)
.Lookup<IdOnly, MongoContentEntity, IdOnly>(Collection, x => x.Id, x => x.Id, x => x.Joined)
.Lookup<IdOnly, MongoContentEntity, IdOnly>(Collection, x => x.Id, x => x.DocumentId, x => x.Joined)
.ToListAsync();
return joined.Select(x => x.Joined[0]).ToList();
@ -146,20 +145,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return query.Sort?.All(x => x.Path.ToString() == "mt" && x.Order == SortOrder.Descending) == true;
}
private static FilterDefinition<MongoContentEntity> CreateFilter(Guid schemaId, ICollection<Guid>? ids, ClrQuery? query)
private static FilterDefinition<MongoContentEntity> CreateFilter(DomainId appId, DomainId schemaId, ICollection<DomainId>? ids, ClrQuery? query)
{
var filters = new List<FilterDefinition<MongoContentEntity>>
{
Filter.Eq(x => x.IndexedAppId, appId),
Filter.Eq(x => x.IndexedSchemaId, schemaId),
Filter.Ne(x => x.IsDeleted, true)
};
if (ids != null && ids.Count > 0)
{
var documentIds = ids.Select(x => DomainId.Combine(appId, x)).ToList();
filters.Add(
Filter.Or(
Filter.AnyIn(x => x.ReferencedIds, ids),
Filter.In(x => x.Id, ids)));
Filter.AnyIn(x => x.ReferencedIds, documentIds),
Filter.In(x => x.DocumentId, documentIds)));
}
if (query?.Filter != null)

24
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryIdsAsync.cs

@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
@ -20,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
internal sealed class QueryIdsAsync : OperationBase
{
private static readonly List<(Guid SchemaId, Guid Id)> EmptyIds = new List<(Guid SchemaId, Guid Id)>();
private static readonly IReadOnlyList<(DomainId SchemaId, DomainId Id)> EmptyIds = new List<(DomainId SchemaId, DomainId Id)>();
private static readonly Lazy<string> IdField = new Lazy<string>(GetIdField);
private static readonly Lazy<string> SchemaIdField = new Lazy<string>(GetSchemaIdField);
private readonly IAppProvider appProvider;
@ -34,28 +35,30 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
{
var index =
new CreateIndexModel<MongoContentEntity>(Index
.Ascending(x => x.IndexedAppId)
.Ascending(x => x.IndexedSchemaId)
.Ascending(x => x.IsDeleted));
return Collection.Indexes.CreateOneAsync(index, cancellationToken: ct);
}
public async Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> DoAsync(Guid appId, HashSet<Guid> ids)
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> DoAsync(DomainId appId, HashSet<DomainId> ids)
{
var documentIds = ids.Select(x => DomainId.Combine(appId, x));
var filter =
Filter.And(
Filter.Eq(x => x.IndexedAppId, appId),
Filter.In(x => x.Id, ids),
Filter.In(x => x.DocumentId, documentIds),
Filter.Ne(x => x.IsDeleted, true));
var contentEntities =
await Collection.Find(filter).Only(x => x.Id, x => x.IndexedSchemaId)
.ToListAsync();
return contentEntities.Select(x => (Guid.Parse(x[SchemaIdField.Value].AsString), Guid.Parse(x[IdField.Value].AsString))).ToList();
return contentEntities.Select(x => (DomainId.Create(x[SchemaIdField.Value].AsString), DomainId.Create(x[IdField.Value].AsString))).ToList();
}
public async Task<IReadOnlyList<(Guid SchemaId, Guid Id)>> DoAsync(Guid appId, Guid schemaId, FilterNode<ClrValue> filterNode)
public async Task<IReadOnlyList<(DomainId SchemaId, DomainId Id)>> DoAsync(DomainId appId, DomainId schemaId, FilterNode<ClrValue> filterNode)
{
var schema = await appProvider.GetSchemaAsync(appId, schemaId);
@ -64,19 +67,20 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations
return EmptyIds;
}
var filter = BuildFilter(filterNode.AdjustToModel(schema.SchemaDef), schemaId);
var filter = BuildFilter(filterNode.AdjustToModel(schema.SchemaDef), appId, schemaId);
var contentEntities =
await Collection.Find(filter).Only(x => x.Id, x => x.IndexedSchemaId)
await Collection.Find(filter).Only(x => x.DocumentId, x => x.IndexedSchemaId)
.ToListAsync();
return contentEntities.Select(x => (Guid.Parse(x[SchemaIdField.Value].AsString), Guid.Parse(x[IdField.Value].AsString))).ToList();
return contentEntities.Select(x => (DomainId.Create(x[SchemaIdField.Value].AsString), DomainId.Create(x[IdField.Value].AsString))).ToList();
}
public static FilterDefinition<MongoContentEntity> BuildFilter(FilterNode<ClrValue>? filterNode, Guid schemaId)
public static FilterDefinition<MongoContentEntity> BuildFilter(FilterNode<ClrValue>? filterNode, DomainId appId, DomainId schemaId)
{
var filters = new List<FilterDefinition<MongoContentEntity>>
{
Filter.Eq(x => x.IndexedAppId, appId),
Filter.Eq(x => x.IndexedSchemaId, schemaId),
Filter.Ne(x => x.IsDeleted, true)
};

12
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/StatusSerializer.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System.Threading;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using Squidex.Domain.Apps.Core.Contents;
@ -14,13 +15,20 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public sealed class StatusSerializer : SerializerBase<Status>
{
private static volatile int isRegistered;
private static int isRegistered;
public static void Register()
{
if (Interlocked.Increment(ref isRegistered) == 1)
{
BsonSerializer.RegisterSerializer(new StatusSerializer());
try
{
BsonSerializer.RegisterSerializer(new StatusSerializer());
}
catch (BsonSerializationException)
{
return;
}
}
}

3
backend/src/Squidex.Domain.Apps.Entities.MongoDb/FullText/MongoIndexStorage.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
@ -29,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.FullText
this.bucket = bucket;
}
public async Task<LuceneDirectory> CreateDirectoryAsync(Guid ownerId)
public async Task<LuceneDirectory> CreateDirectoryAsync(DomainId ownerId)
{
var fileId = $"index_{ownerId}";

61
backend/src/Squidex.Domain.Apps.Entities.MongoDb/FullText/MongoTextIndexState.cs

@ -0,0 +1,61 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Entities.Contents.Text.State;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.MongoDb.FullText
{
public sealed class MongoTextIndexState
{
[BsonId]
[BsonElement]
public DomainId DocumentId { get; set; }
[BsonRequired]
[BsonElement]
public DomainId ContentId { get; set; }
[BsonRequired]
[BsonElement("c")]
public string DocIdCurrent { get; set; }
[BsonRequired]
[BsonElement("n")]
public string? DocIdNew { get; set; }
[BsonRequired]
[BsonElement("p")]
public string? DocIdForPublished { get; set; }
public MongoTextIndexState()
{
}
public MongoTextIndexState(DomainId documentId, TextContentState state)
{
DocumentId = documentId;
ContentId = state.ContentId;
DocIdNew = state.DocIdNew;
DocIdCurrent = state.DocIdCurrent;
DocIdForPublished = state.DocIdForPublished;
}
public TextContentState ToState()
{
return new TextContentState
{
ContentId = ContentId,
DocIdNew = DocIdNew,
DocIdCurrent = DocIdCurrent,
DocIdForPublished = DocIdForPublished
};
}
}
}

43
backend/src/Squidex.Domain.Apps.Entities.MongoDb/FullText/MongoTextIndexerState.cs

@ -5,34 +5,16 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Contents.Text.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.FullText
{
public sealed class MongoTextIndexerState : MongoRepositoryBase<TextContentState>, ITextIndexerState
public sealed class MongoTextIndexerState : MongoRepositoryBase<MongoTextIndexState>, ITextIndexerState
{
static MongoTextIndexerState()
{
BsonClassMap.RegisterClassMap<TextContentState>(cm =>
{
cm.MapIdField(x => x.ContentId);
cm.MapProperty(x => x.DocIdCurrent)
.SetElementName("c");
cm.MapProperty(x => x.DocIdNew)
.SetElementName("n").SetIgnoreIfNull(true);
cm.MapProperty(x => x.DocIdForPublished)
.SetElementName("p").SetIgnoreIfNull(true);
});
}
public MongoTextIndexerState(IMongoDatabase database, bool setup = false)
: base(database, setup)
{
@ -43,19 +25,28 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.FullText
return "TextIndexerState";
}
public Task<TextContentState?> GetAsync(Guid contentId)
public async Task<TextContentState?> GetAsync(DomainId appId, DomainId contentId)
{
return Collection.Find(x => x.ContentId == contentId).FirstOrDefaultAsync()!;
var documentId = DomainId.Combine(appId, contentId).ToString();
var result = await Collection.Find(x => x.DocumentId == documentId).FirstOrDefaultAsync()!;
return result?.ToState();
}
public Task RemoveAsync(Guid contentId)
public Task RemoveAsync(DomainId appId, DomainId contentId)
{
return Collection.DeleteOneAsync(x => x.ContentId == contentId);
var documentId = DomainId.Combine(appId, contentId).ToString();
return Collection.DeleteOneAsync(x => x.DocumentId == documentId);
}
public Task SetAsync(TextContentState state)
public Task SetAsync(DomainId appId, TextContentState state)
{
return Collection.ReplaceOneAsync(x => x.ContentId == state.ContentId, state, UpsertReplace);
var documentId = DomainId.Combine(appId, state.ContentId).ToString();
var document = new MongoTextIndexState(documentId, state);
return Collection.ReplaceOneAsync(x => x.DocumentId == documentId, document, UpsertReplace);
}
}
}

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -13,6 +12,7 @@ using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Entities.History.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.History
@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.History
cancellationToken: ct);
}
public async Task<IReadOnlyList<HistoryEvent>> QueryByChannelAsync(Guid appId, string channelPrefix, int count)
public async Task<IReadOnlyList<HistoryEvent>> QueryByChannelAsync(DomainId appId, string channelPrefix, int count)
{
if (!string.IsNullOrWhiteSpace(channelPrefix))
{
@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.History
return Collection.ReplaceOneAsync(x => x.Id == item.Id, item, UpsertReplace);
}
public Task RemoveAsync(Guid appId)
public Task RemoveAsync(DomainId appId)
{
return Collection.DeleteManyAsync(x => x.AppId == appId);
}

18
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventEntity.cs

@ -5,13 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
@ -20,13 +20,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
{
[BsonRequired]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid AppId { get; set; }
public DomainId AppId { get; set; }
[BsonIgnoreIfDefault]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid RuleId { get; set; }
public DomainId RuleId { get; set; }
[BsonRequired]
[BsonElement]
@ -58,5 +56,15 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
[BsonRequired]
[BsonElement]
public Instant? NextAttempt { get; set; }
DomainId IEntity.Id
{
get { return DocumentId; }
}
DomainId IEntity.UniqueId
{
get { return DocumentId; }
}
}
}

32
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs

@ -43,8 +43,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
await collection.Indexes.CreateManyAsync(new[]
{
new CreateIndexModel<MongoRuleEventEntity>(Index.Ascending(x => x.NextAttempt)),
new CreateIndexModel<MongoRuleEventEntity>(Index.Ascending(x => x.AppId).Descending(x => x.Created)),
new CreateIndexModel<MongoRuleEventEntity>(
Index.Ascending(x => x.NextAttempt)),
new CreateIndexModel<MongoRuleEventEntity>(
Index.Ascending(x => x.AppId).Descending(x => x.Created)),
new CreateIndexModel<MongoRuleEventEntity>(
Index
.Ascending(x => x.Expires),
@ -60,13 +64,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
return Collection.Find(x => x.NextAttempt < now).ForEachAsync(callback, ct);
}
public async Task<IResultList<IRuleEventEntity>> QueryByAppAsync(Guid appId, Guid? ruleId = null, int skip = 0, int take = 20)
public async Task<IResultList<IRuleEventEntity>> QueryByAppAsync(DomainId appId, DomainId? ruleId = null, int skip = 0, int take = 20)
{
var filter = Filter.Eq(x => x.AppId, appId);
if (ruleId.HasValue)
if (ruleId.HasValue && ruleId.Value != DomainId.Empty)
{
filter = Filter.And(filter, Filter.Eq(x => x.RuleId, ruleId));
filter = Filter.And(filter, Filter.Eq(x => x.RuleId, ruleId.Value));
}
var taskForItems = Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.Created).ToListAsync();
@ -77,18 +81,18 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
return ResultList.Create(total, items);
}
public async Task<IRuleEventEntity> FindAsync(Guid id)
public async Task<IRuleEventEntity> FindAsync(DomainId id)
{
var ruleEvent =
await Collection.Find(x => x.Id == id)
await Collection.Find(x => x.DocumentId == id)
.FirstOrDefaultAsync();
return ruleEvent;
}
public Task EnqueueAsync(Guid id, Instant nextAttempt)
public Task EnqueueAsync(DomainId id, Instant nextAttempt)
{
return Collection.UpdateOneAsync(x => x.Id == id, Update.Set(x => x.NextAttempt, nextAttempt));
return Collection.UpdateOneAsync(x => x.DocumentId == id, Update.Set(x => x.NextAttempt, nextAttempt));
}
public async Task EnqueueAsync(RuleJob job, Instant? nextAttempt, CancellationToken ct = default)
@ -98,9 +102,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
await Collection.InsertOneIfNotExistsAsync(entity, ct);
}
public Task CancelAsync(Guid id)
public Task CancelAsync(DomainId id)
{
return Collection.UpdateOneAsync(x => x.Id == id,
return Collection.UpdateOneAsync(x => x.DocumentId == id,
Update
.Set(x => x.NextAttempt, null)
.Set(x => x.JobResult, RuleJobResult.Cancelled));
@ -120,7 +124,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
await statisticsCollection.IncrementFailed(job.AppId, job.RuleId, update.Finished);
}
await Collection.UpdateOneAsync(x => x.Id == job.Id,
var documentId = job.Id.ToString();
await Collection.UpdateOneAsync(x => x.DocumentId == documentId,
Update
.Set(x => x.Result, update.ExecutionResult)
.Set(x => x.LastDump, update.ExecutionDump)
@ -129,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
.Inc(x => x.NumCalls, 1));
}
public Task<IReadOnlyList<RuleStatistics>> QueryStatisticsByAppAsync(Guid appId)
public Task<IReadOnlyList<RuleStatistics>> QueryStatisticsByAppAsync(DomainId appId)
{
return statisticsCollection.QueryByAppAsync(appId);
}

15
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleStatisticsCollection.cs

@ -5,16 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver;
using NodaTime;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
@ -23,15 +21,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
{
static MongoRuleStatisticsCollection()
{
var guidSerializer = new GuidSerializer().WithRepresentation(BsonType.String);
BsonClassMap.RegisterClassMap<RuleStatistics>(cm =>
{
cm.AutoMap();
cm.MapProperty(x => x.AppId).SetSerializer(guidSerializer);
cm.MapProperty(x => x.RuleId).SetSerializer(guidSerializer);
cm.SetIgnoreExtraElements(true);
});
}
@ -56,14 +49,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
cancellationToken: ct);
}
public async Task<IReadOnlyList<RuleStatistics>> QueryByAppAsync(Guid appId)
public async Task<IReadOnlyList<RuleStatistics>> QueryByAppAsync(DomainId appId)
{
var statistics = await Collection.Find(x => x.AppId == appId).ToListAsync();
return statistics;
}
public Task IncrementSuccess(Guid appId, Guid ruleId, Instant now)
public Task IncrementSuccess(DomainId appId, DomainId ruleId, Instant now)
{
return Collection.UpdateOneAsync(
x => x.AppId == appId && x.RuleId == ruleId,
@ -75,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
Upsert);
}
public Task IncrementFailed(Guid appId, Guid ruleId, Instant now)
public Task IncrementFailed(DomainId appId, DomainId ruleId, Instant now)
{
return Collection.UpdateOneAsync(
x => x.AppId == appId && x.RuleId == ruleId,

15
backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Apps;
@ -40,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities
this.indexSchemas = indexSchemas;
}
public Task<(IAppEntity?, ISchemaEntity?)> GetAppWithSchemaAsync(Guid appId, Guid id)
public Task<(IAppEntity?, ISchemaEntity?)> GetAppWithSchemaAsync(DomainId appId, DomainId id)
{
return localCache.GetOrCreateAsync($"GetAppWithSchemaAsync({appId}, {id})", async () =>
{
@ -48,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities
});
}
private async Task<(IAppEntity?, ISchemaEntity?)> GetAppWithSchemaUncachedAsync(Guid appId, Guid id)
private async Task<(IAppEntity?, ISchemaEntity?)> GetAppWithSchemaUncachedAsync(DomainId appId, DomainId id)
{
var app = await GetAppAsync(appId);
@ -67,7 +66,7 @@ namespace Squidex.Domain.Apps.Entities
return (app, schema);
}
public Task<IAppEntity?> GetAppAsync(Guid appId)
public Task<IAppEntity?> GetAppAsync(DomainId appId)
{
return localCache.GetOrCreateAsync($"GetAppAsync({appId})", async () =>
{
@ -91,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities
});
}
public Task<ISchemaEntity?> GetSchemaAsync(Guid appId, string name)
public Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, string name)
{
return localCache.GetOrCreateAsync($"GetSchemaAsync({appId}, {name})", async () =>
{
@ -99,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities
});
}
public Task<ISchemaEntity?> GetSchemaAsync(Guid appId, Guid id, bool allowDeleted = false)
public Task<ISchemaEntity?> GetSchemaAsync(DomainId appId, DomainId id, bool allowDeleted = false)
{
return localCache.GetOrCreateAsync($"GetSchemaAsync({appId}, {id}, {allowDeleted})", async () =>
{
@ -107,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities
});
}
public Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId)
public Task<List<ISchemaEntity>> GetSchemasAsync(DomainId appId)
{
return localCache.GetOrCreateAsync($"GetSchemasAsync({appId})", async () =>
{
@ -115,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities
});
}
public Task<List<IRuleEntity>> GetRulesAsync(Guid appId)
public Task<List<IRuleEntity>> GetRulesAsync(DomainId appId)
{
return localCache.GetOrCreateAsync($"GetRulesAsync({appId})", async () =>
{

2
backend/src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs

@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
using (var uploadStream = file.OpenRead())
{
await appImageStore.UploadAsync(uploadImage.AppId, uploadStream);
await appImageStore.UploadAsync(uploadImage.AppId.Id, uploadStream);
}
}
}

35
backend/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs

@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
public AppDomainObject(
InitialPatterns initialPatterns,
IStore<Guid> store,
IStore<DomainId> store,
ISemanticLog log,
IAppPlansProvider appPlansProvider,
IAppPlanBillingManager appPlansBillingManager,
@ -52,10 +52,24 @@ namespace Squidex.Domain.Apps.Entities.Apps
this.initialPatterns = initialPatterns;
}
public override Task<object?> ExecuteAsync(IAggregateCommand command)
protected override bool IsDeleted()
{
VerifyNotArchived();
return Snapshot.IsArchived;
}
protected override bool CanAcceptCreation(ICommand command)
{
return command is CreateApp;
}
protected override bool CanAccept(ICommand command)
{
return command is AppUpdateCommand update &&
Equals(update?.AppId?.Id, Snapshot.Id);
}
public override Task<object?> ExecuteAsync(IAggregateCommand command)
{
switch (command)
{
case CreateApp createApp:
@ -466,20 +480,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
RaiseEvent(SimpleMapper.Map(command, new AppArchived()));
}
private void VerifyNotArchived()
{
if (Snapshot.IsArchived)
{
throw new DomainException("App has already been archived.");
}
}
private void RaiseEvent(AppEvent @event)
{
if (@event.AppId == null)
{
@event.AppId = NamedId.Of(Snapshot.Id, Snapshot.Name);
}
@event.AppId ??= Snapshot.NamedId();
RaiseEvent(Envelope.Create(@event));
}
@ -489,7 +492,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
return new AppCreated { Name = name };
}
private static AppPatternAdded CreateInitialPattern(Guid id, AppPattern pattern)
private static AppPatternAdded CreateInitialPattern(DomainId id, AppPattern pattern)
{
return new AppPatternAdded { PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message };
}

5
backend/src/Squidex.Domain.Apps.Entities/Apps/AppExtensions.cs

@ -5,16 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps
{
public static class AppExtensions
{
public static NamedId<Guid> NamedId(this IAppEntity app)
public static NamedId<DomainId> NamedId(this IAppEntity app)
{
return new NamedId<Guid>(app.Id, app.Name);
return new NamedId<DomainId>(app.Id, app.Name);
}
}
}

3
backend/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Events.Apps;
@ -133,7 +132,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
return ForEvent(e, "settings.roles").Param("Name", name);
}
private HistoryEvent CreatePatternsEvent(IEvent e, Guid id, string? name = null)
private HistoryEvent CreatePatternsEvent(IEvent e, DomainId id, string? name = null)
{
return ForEvent(e, "settings.patterns").Param("PatternId", id).Param("Name", name);
}

2
backend/src/Squidex.Domain.Apps.Entities/Apps/AppSettingsSearchSource.cs

@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
var appId = context.App.NamedId();
void Search(string term, string permissionId, Func<NamedId<Guid>, string> generate, SearchResultType type)
void Search(string term, string permissionId, Func<NamedId<DomainId>, string> generate, SearchResultType type)
{
if (result.Count < MaxItems && term.Contains(query, StringComparison.OrdinalIgnoreCase))
{

13
backend/src/Squidex.Domain.Apps.Entities/Apps/AppUISettings.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Orleans;
using Squidex.Infrastructure;
@ -25,34 +24,34 @@ namespace Squidex.Domain.Apps.Entities.Apps
this.grainFactory = grainFactory;
}
public async Task<JsonObject> GetAsync(Guid appId, string? userId)
public async Task<JsonObject> GetAsync(DomainId appId, string? userId)
{
var result = await GetGrain(appId, userId).GetAsync();
return result.Value;
}
public Task RemoveAsync(Guid appId, string? userId, string path)
public Task RemoveAsync(DomainId appId, string? userId, string path)
{
return GetGrain(appId, userId).RemoveAsync(path);
}
public Task SetAsync(Guid appId, string? userId, string path, IJsonValue value)
public Task SetAsync(DomainId appId, string? userId, string path, IJsonValue value)
{
return GetGrain(appId, userId).SetAsync(path, value.AsJ());
}
public Task SetAsync(Guid appId, string? userId, JsonObject settings)
public Task SetAsync(DomainId appId, string? userId, JsonObject settings)
{
return GetGrain(appId, userId).SetAsync(settings.AsJ());
}
private IAppUISettingsGrain GetGrain(Guid appId, string? userId)
private IAppUISettingsGrain GetGrain(DomainId appId, string? userId)
{
return grainFactory.GetGrain<IAppUISettingsGrain>(GetKey(appId, userId));
}
private static string GetKey(Guid appId, string? userId)
private static string GetKey(DomainId appId, string? userId)
{
if (!string.IsNullOrWhiteSpace(userId))
{

9
backend/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
@ -115,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
await appUISettings.SetAsync(context.AppId, null, json);
}
private async Task ReserveAppAsync(Guid appId, string appName)
private async Task ReserveAppAsync(DomainId appId, string appName)
{
appReservation = await appsIndex.ReserveAsync(appId, appName);
@ -125,7 +124,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
}
}
public async Task CleanupRestoreErrorAsync(Guid appId)
public async Task CleanupRestoreErrorAsync(DomainId appId)
{
if (appReservation != null)
{
@ -140,7 +139,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
await appsIndex.RebuildByContributorsAsync(context.AppId, contributors);
}
private Task WriteAssetAsync(Guid appId, IBackupWriter writer)
private Task WriteAssetAsync(DomainId appId, IBackupWriter writer)
{
return writer.WriteBlobAsync(AvatarFile, async stream =>
{
@ -154,7 +153,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
});
}
private async Task ReadAssetAsync(Guid appId, IBackupReader reader)
private async Task ReadAssetAsync(DomainId appId, IBackupReader reader)
{
try
{

2
backend/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs

@ -9,7 +9,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AddLanguage : AppCommand
public sealed class AddLanguage : AppUpdateCommand
{
public Language Language { get; set; }
}

8
backend/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddPattern.cs

@ -5,13 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AddPattern : AppCommand
public sealed class AddPattern : AppUpdateCommand
{
public Guid PatternId { get; set; }
public DomainId PatternId { get; set; }
public string Name { get; set; }
@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands
public AddPattern()
{
PatternId = Guid.NewGuid();
PatternId = DomainId.NewGuid();
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save