Skip to content

Commit

Permalink
6.8 bugfixes (#897)
Browse files Browse the repository at this point in the history
* Several bugfixes.

* Fix scheduler and release semaphore.

* Disable delete button if the user does not have permissions.

* Fix concurrency issues with cache grains.

* Map translate code.

* Just some formatting.
  • Loading branch information
SebastianStehle authored Jul 14, 2022
1 parent 8d429a0 commit e337e41
Show file tree
Hide file tree
Showing 22 changed files with 235 additions and 44 deletions.
1 change: 1 addition & 0 deletions backend/i18n/source/backend_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
"contents.singletonNotChangeable": "Singleton content cannot be updated.",
"contents.singletonNotCreatable": "Singleton content cannot be created.",
"contents.singletonNotDeletable": "Singleton content cannot be deleted.",
"contents.componentNotCreatable": "Component content cannot be created.",
"contents.statusNotValid": "Status is not defined in the workflow.",
"contents.statusTransitionNotAllowed": "Cannot change status from {oldStatus} to {newStatus}.",
"contents.validation.aspectRatio": "Must have aspect ratio {width}:{height}.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ public async Task<IReadOnlyCollection<DomainId>> GetAppIdsAsync(string[] names)

result.Add(id);
}
else if (appIds.TryGetValue(name, out var cachedId) && cachedId != DomainId.Empty)
{
result.Add(cachedId);
}
else
{
appIds[name] = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ public override Task<CommandResult> ExecuteAsync(IAggregateCommand command)

private async Task CreateCore(CreateContent c, ContentOperation operation)
{
operation.MustNotCreateComponent();
operation.MustNotCreateSingleton();
operation.MustNotCreateForUnpublishedSchema();
operation.MustHaveData(c.Data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ public static void MustNotCreateForUnpublishedSchema(this ContentOperation opera
}
}

public static void MustNotCreateComponent(this ContentOperation operation)
{
if (operation.SchemaDef.Type == SchemaType.Component)
{
throw new DomainException(T.Get("contents.componentNotCreatable"));
}
}

public static void MustNotCreateSingleton(this ContentOperation operation)
{
if (operation.SchemaDef.Type == SchemaType.Singleton && operation.CommandId != operation.Schema.Id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,49 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
public sealed class RulesCacheGrain : GrainBase, IRulesCacheGrain
{
private readonly IRuleRepository ruleRepository;
private List<DomainId>? ruleIds;
private readonly HashSet<DomainId> ruleIds = new HashSet<DomainId>();
private bool isLoaded;

public RulesCacheGrain(IGrainIdentity grainIdentity, IRuleRepository ruleRepository)
: base(grainIdentity)
{
this.ruleRepository = ruleRepository;
}

public async Task<IReadOnlyCollection<DomainId>> GetRuleIdsAsync()
public override Task OnActivateAsync()
{
var ids = ruleIds;
return GetRuleIdsAsync();
}

if (ids == null)
public async Task<IReadOnlyCollection<DomainId>> GetRuleIdsAsync()
{
if (!isLoaded)
{
ids = await ruleRepository.QueryIdsAsync(Key);
var loaded = await ruleRepository.QueryIdsAsync(Key);

foreach (var id in loaded)
{
ruleIds.Add(id);
}

ruleIds = ids;
isLoaded = true;
}

return ids;
return ruleIds;
}

public Task AddAsync(DomainId id)
{
ruleIds?.Add(id);
ruleIds.Add(id);

return Task.CompletedTask;
}

public Task RemoveAsync(DomainId id)
public async Task RemoveAsync(DomainId id)
{
ruleIds?.Remove(id);
await GetRuleIdsAsync();

return Task.CompletedTask;
ruleIds.Remove(id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
public sealed class SchemasCacheGrain : UniqueNameGrain<DomainId>, ISchemasCacheGrain
{
private readonly ISchemaRepository schemaRepository;
private Dictionary<string, DomainId>? schemaIds;
private readonly Dictionary<string, DomainId> schemaIds = new Dictionary<string, DomainId>();
private bool isLoaded;

public SchemasCacheGrain(IGrainIdentity identity, ISchemaRepository schemaRepository)
: base(identity)
{
this.schemaRepository = schemaRepository;
}

public override Task OnActivateAsync()
{
return GetIdsAsync();
}

public override async Task<string?> ReserveAsync(DomainId id, string name)
{
var token = await base.ReserveAsync(id, name);
Expand Down Expand Up @@ -61,41 +67,38 @@ public async Task<DomainId> GetSchemaIdAsync(string name)

private async Task<Dictionary<string, DomainId>> GetIdsAsync()
{
var ids = schemaIds;

if (ids == null)
if (!isLoaded)
{
ids = await schemaRepository.QueryIdsAsync(Key);
var loaded = await schemaRepository.QueryIdsAsync(Key);

schemaIds = ids;
foreach (var (name, id) in loaded)
{
schemaIds[name] = id;
}

isLoaded = true;
}

return ids;
return schemaIds;
}

public Task AddAsync(DomainId id, string name)
{
if (schemaIds != null)
{
schemaIds[name] = id;
}
schemaIds[name] = id;

return Task.CompletedTask;
}

public Task RemoveAsync(DomainId id)
public async Task RemoveAsync(DomainId id)
{
if (schemaIds != null)
{
var name = schemaIds.FirstOrDefault(x => x.Value == id).Key;
await GetIdsAsync();

if (name != null)
{
schemaIds.Remove(name);
}
}
var name = schemaIds.FirstOrDefault(x => x.Value == id).Key;

return Task.CompletedTask;
if (name != null)
{
schemaIds.Remove(name);
}
}
}
}
2 changes: 2 additions & 0 deletions backend/src/Squidex.Infrastructure/Tasks/Scheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ private async Task ScheduleTask(SchedulerTask task,
}
finally
{
semaphore.Release();

if (Interlocked.Decrement(ref pendingTasks) <= 1)
{
tcs.TrySetResult(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,12 @@ protected virtual void CreateLinks(Resources resources)

AddSelfLink(resources.Url<SchemasController>(x => nameof(x.GetSchema), values));

if (resources.CanReadContent(Name))
if (resources.CanReadContent(Name) && Type == SchemaType.Default)
{
AddGetLink("contents", resources.Url<ContentsController>(x => nameof(x.GetContents), values));
}

if (resources.CanCreateContent(Name))
if (resources.CanCreateContent(Name) && Type == SchemaType.Default)
{
AddPostLink("contents/create", resources.Url<ContentsController>(x => nameof(x.PostContent), values));
AddPostLink("contents/create/publish", resources.Url<ContentsController>(x => nameof(x.PostContent), values) + "?publish=true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public sealed class TranslationDto

public static TranslationDto FromDomain(TranslationResult translation)
{
return SimpleMapper.Map(translation, new TranslationDto());
return SimpleMapper.Map(translation, new TranslationDto { Result = translation.Code });
}
}
}
3 changes: 3 additions & 0 deletions backend/src/Squidex/Config/Domain/BackupsServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public static void AddSquidexBackups(this IServiceCollection services)
services.AddSingletonAs<TempFolderBackupArchiveLocation>()
.As<IBackupArchiveLocation>();

services.AddSingletonAs<DefaultBackupHandlerFactory>()
.As<IBackupHandlerFactory>();

services.AddSingletonAs<DefaultBackupArchiveStore>()
.As<IBackupArchiveStore>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,27 @@ public async Task Should_remove_id_from_loaded_result()
A.CallTo(() => appRepository.QueryIdsAsync(A<IEnumerable<string>>.That.Is("name1", "name2"), default))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task Should_merge_found_value_with_added_id()
{
var foundId = DomainId.NewGuid();

async Task<Dictionary<string, DomainId>> GetIds()
{
await sut.AddAsync(foundId, "name1");

return new Dictionary<string, DomainId>();
}

A.CallTo(() => appRepository.QueryIdsAsync(A<IEnumerable<string>>._, A<CancellationToken>._))
.ReturnsLazily(() => GetIds());

var result1 = await sut.GetAppIdsAsync(new[] { "name1" });
var result2 = await sut.GetAppIdsAsync(new[] { "name1" });

Assert.Equal(foundId, result1.Single());
Assert.Equal(foundId, result2.Single());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class GuardContentTests : IClassFixture<TranslationsFixture>
private readonly ISchemaEntity normalUnpublishedSchema;
private readonly ISchemaEntity singletonSchema;
private readonly ISchemaEntity singletonUnpublishedSchema;
private readonly ISchemaEntity componentSchema;
private readonly RefToken actor = RefToken.User("123");

public GuardContentTests()
Expand All @@ -49,6 +50,9 @@ public GuardContentTests()

singletonSchema =
Mocks.Schema(appId, schemaId, new Schema(schemaId.Name, type: SchemaType.Singleton).Publish());

componentSchema =
Mocks.Schema(appId, schemaId, new Schema(schemaId.Name, type: SchemaType.Component).Publish());
}

[Fact]
Expand Down Expand Up @@ -91,6 +95,14 @@ public void Should_throw_exception_if_creating_singleton_content()
Assert.Throws<DomainException>(() => operation.MustNotCreateSingleton());
}

[Fact]
public void Should_throw_exception_if_creating_component_content()
{
var operation = Operation(CreateContent(Status.Draft), componentSchema);

Assert.Throws<DomainException>(() => operation.MustNotCreateComponent());
}

[Fact]
public void Should_not_throw_exception_if_creating_singleton_content_with_schema_id()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,51 @@ public async Task Should_remove_id_from_loaded_result()
A.CallTo(() => ruleRepository.QueryIdsAsync(appId, default))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task Should_remove_id_from_not_loaded_result()
{
var ids = new List<DomainId>
{
DomainId.NewGuid(),
DomainId.NewGuid()
};

var newId = DomainId.NewGuid();

A.CallTo(() => ruleRepository.QueryIdsAsync(appId, default))
.Returns(ids);

await sut.RemoveAsync(ids.ElementAt(0));

var result = await sut.GetRuleIdsAsync();

Assert.Equal(ids.Skip(1), result);

A.CallTo(() => ruleRepository.QueryIdsAsync(appId, default))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task Should_merge_found_value_with_added_id()
{
var foundId = DomainId.NewGuid();

async Task<List<DomainId>> GetIds()
{
await sut.AddAsync(foundId);

return new List<DomainId>();
}

A.CallTo(() => ruleRepository.QueryIdsAsync(appId, default))
.ReturnsLazily(() => GetIds());

var result1 = await sut.GetRuleIdsAsync();
var result2 = await sut.GetRuleIdsAsync();

Assert.Equal(foundId, result1.Single());
Assert.Equal(foundId, result2.Single());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,56 @@ public async Task Should_remove_id_from_loaded_result()

var result = await sut.GetSchemaIdsAsync();

Assert.Equal(ids.Values.Take(1), result);
Assert.Equal(ids.Values.Skip(1), result);

A.CallTo(() => schemaRepository.QueryIdsAsync(appId, default))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task Should_remove_id_from_not_loaded_result()
{
var ids = new Dictionary<string, DomainId>
{
["name1"] = DomainId.NewGuid(),
["name2"] = DomainId.NewGuid()
};

var newId = DomainId.NewGuid();

A.CallTo(() => schemaRepository.QueryIdsAsync(appId, default))
.Returns(ids);

await sut.RemoveAsync(ids.ElementAt(0).Value);

var result = await sut.GetSchemaIdsAsync();

Assert.Equal(ids.Values.Skip(1), result);

A.CallTo(() => schemaRepository.QueryIdsAsync(appId, default))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task Should_merge_found_value_with_added_id()
{
var foundId = DomainId.NewGuid();

async Task<Dictionary<string, DomainId>> GetIds()
{
await sut.AddAsync(foundId, "name1");

return new Dictionary<string, DomainId>();
}

A.CallTo(() => schemaRepository.QueryIdsAsync(appId, default))
.ReturnsLazily(() => GetIds());

var result1 = await sut.GetSchemaIdsAsync();
var result2 = await sut.GetSchemaIdsAsync();

Assert.Equal(foundId, result1.Single());
Assert.Equal(foundId, result2.Single());
}
}
}
Loading

0 comments on commit e337e41

Please sign in to comment.