Browse Source

Merge branch 'master' of github.com:Squidex/squidex

pull/1122/head
Sebastian Stehle 2 years ago
parent
commit
49d039ce1c
  1. 14
      .github/workflows/dev.yml
  2. 8
      .github/workflows/make-screenshots.yml
  3. 12
      .github/workflows/release.yml
  4. 12
      backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs
  5. 2
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetSlug.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContents.cs
  7. 1
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/EnrichContentDefaults.cs
  8. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/UpsertContent.cs
  9. 12
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentHeaders.cs
  10. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs
  11. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs
  12. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs
  13. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  14. 2
      backend/src/Squidex.Domain.Apps.Entities/Jobs/JobWorker.cs
  15. 12
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs
  16. 2
      backend/src/Squidex.Infrastructure/Migrations/Migrator.cs
  17. 4
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  18. 5
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs
  19. 29
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/EnrichContentDefaultsDto.cs
  20. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichForCachingTests.cs
  21. 1
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichForCachingTests.cs
  22. 3
      helm/squidex/templates/service.yaml
  23. 3
      helm/squidex/values.yaml
  24. 3
      tools/.editorconfig
  25. 2
      tools/TestSuite/TestSuite.ApiTests/AssetFoldersTests.cs
  26. 2
      tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  27. 155
      tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  28. 15
      tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs
  29. 20
      tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj
  30. 8
      tools/TestSuite/TestSuite.LoadTests/TestSuite.LoadTests.csproj
  31. 2
      tools/TestSuite/TestSuite.Shared/ClientExtensions.cs
  32. 49
      tools/TestSuite/TestSuite.Shared/ContentStrategies.cs
  33. 14
      tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj

14
.github/workflows/dev.yml

@ -22,10 +22,10 @@ jobs:
uses: rlespinasse/github-slug-action@v4.5.0
- name: Prepare - Setup QEMU
uses: docker/setup-qemu-action@v3.1.0
uses: docker/setup-qemu-action@v3.2.0
- name: Prepare - Setup Docker Buildx
uses: docker/setup-buildx-action@v3.4.0
uses: docker/setup-buildx-action@v3.6.1
- name: Prepare - Setup Node
uses: actions/setup-node@v4.0.3
@ -33,7 +33,7 @@ jobs:
node-version: 18
- name: Build - BUILD
uses: docker/build-push-action@v6.4.1
uses: docker/build-push-action@v6.7.0
with:
load: true
build-args: "SQUIDEX__RUNTIME__VERSION=7.0.0-dev-${{ env.BUILD_NUMBER }}"
@ -103,7 +103,7 @@ jobs:
- name: Test - Upload Playwright Artifacts
if: always()
uses: actions/upload-artifact@v4.3.4
uses: actions/upload-artifact@v4.3.6
with:
name: playwright-report
path: tools/e2e/playwright-report/
@ -111,7 +111,7 @@ jobs:
- name: Test - Upload Screenshots
if: failure()
uses: actions/upload-artifact@v4.3.4
uses: actions/upload-artifact@v4.3.6
with:
path: |
tools/TestSuite/TestSuite.ApiTests/bin/Debug/net8.0/screenshots/
@ -142,14 +142,14 @@ jobs:
- name: Publish - Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3.2.0
uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish - Build & Push for Multi-Platforms
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v6.4.1
uses: docker/build-push-action@v6.7.0
with:
build-args: "SQUIDEX__RUNTIME__VERSION=7.0.0-dev-${{ env.BUILD_NUMBER }}"
cache-from: type=gha

8
.github/workflows/make-screenshots.yml

@ -12,10 +12,10 @@ jobs:
uses: actions/checkout@v4.1.7
- name: Prepare - Setup QEMU
uses: docker/setup-qemu-action@v3.1.0
uses: docker/setup-qemu-action@v3.2.0
- name: Prepare - Setup Docker Buildx
uses: docker/setup-buildx-action@v3.4.0
uses: docker/setup-buildx-action@v3.6.1
- name: Prepare - Setup Node
uses: actions/setup-node@v4.0.3
@ -23,7 +23,7 @@ jobs:
node-version: 18
- name: Build - BUILD
uses: docker/build-push-action@v6.4.1
uses: docker/build-push-action@v6.7.0
with:
load: true
cache-from: type=gha
@ -50,7 +50,7 @@ jobs:
- name: Test - Upload Playwright Artifacts
if: always()
uses: actions/upload-artifact@v4.3.4
uses: actions/upload-artifact@v4.3.6
with:
name: snapshots
path: tools/e2e/snapshots/

12
.github/workflows/release.yml

@ -17,10 +17,10 @@ jobs:
uses: rlespinasse/github-slug-action@v4.5.0
- name: Prepare - Setup QEMU
uses: docker/setup-qemu-action@v3.1.0
uses: docker/setup-qemu-action@v3.2.0
- name: Prepare - Setup Docker Buildx
uses: docker/setup-buildx-action@v3.4.0
uses: docker/setup-buildx-action@v3.6.1
- name: Prepare - Setup Node
uses: actions/setup-node@v4.0.3
@ -28,7 +28,7 @@ jobs:
node-version: 18
- name: Build - BUILD
uses: docker/build-push-action@v6.4.1
uses: docker/build-push-action@v6.7.0
with:
load: true
build-args: "SQUIDEX__BUILD__VERSION=${{ env.GITHUB_REF_SLUG }},SQUIDEX__RUNTIME__VERSION=${{ env.GITHUB_REF_SLUG }}"
@ -98,7 +98,7 @@ jobs:
- name: Test - Upload Playwright Artifacts
if: always()
uses: actions/upload-artifact@v4.3.4
uses: actions/upload-artifact@v4.3.6
with:
name: playwright-report
path: tools/e2e/playwright-report/
@ -136,13 +136,13 @@ jobs:
fi
- name: Publish - Login to Docker Hub
uses: docker/login-action@v3.2.0
uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish - Build & Push for Multi-Platforms
uses: docker/build-push-action@v6.4.1
uses: docker/build-push-action@v6.7.0
with:
build-args: "SQUIDEX__BUILD__VERSION=${{ env.GITHUB_REF_SLUG }},SQUIDEX__RUNTIME__VERSION=${{ env.GITHUB_REF_SLUG }}"
cache-from: type=gha

12
backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs

@ -227,11 +227,11 @@ public sealed class AppsIndex : IAppsIndex, ICommandMiddleware, IInitializable
}
// Do not use cancellation here as we already so far.
await appCache.AddAsync(new[]
{
await appCache.AddAsync(
[
new KeyValuePair<string, object?>(GetCacheKey(app.Id), app),
new KeyValuePair<string, object?>(GetCacheKey(app.Name), app),
}, options.CacheDuration);
], options.CacheDuration);
return app;
}
@ -244,10 +244,10 @@ public sealed class AppsIndex : IAppsIndex, ICommandMiddleware, IInitializable
}
// Do not use cancellation here as we already so far.
return appCache.RemoveAsync(new[]
{
return appCache.RemoveAsync(
[
GetCacheKey(id),
GetCacheKey(name)
});
]);
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetSlug.cs

@ -11,7 +11,7 @@ namespace Squidex.Domain.Apps.Entities.Assets;
public static class AssetSlug
{
private static readonly HashSet<char> Dot = new HashSet<char>(new[] { '.' });
private static readonly HashSet<char> Dot = new HashSet<char>(['.']);
public static string ToAssetSlug(this string value)
{

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContents.cs

@ -29,5 +29,7 @@ public sealed class BulkUpdateContents : SquidexCommand, IAppCommand, ISchemaCom
public bool OptimizeValidation { get; set; }
public bool EnrichRequiredFields { get; set; }
public BulkUpdateJob[]? Jobs { get; set; }
}

1
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/EnrichContentDefaults.cs

@ -9,4 +9,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands;
public sealed class EnrichContentDefaults : ContentCommand
{
public bool EnrichRequiredFields { get; set; }
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/UpsertContent.cs

@ -21,6 +21,8 @@ public sealed class UpsertContent : ContentDataCommand, ISchemaCommand
public bool EnrichDefaults { get; set; }
public bool EnrichRequiredFields { get; set; }
public UpsertContent()
{
ContentId = DomainId.NewGuid();

12
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentHeaders.cs

@ -19,6 +19,7 @@ public static class ContentHeaders
public const string KeyLanguages = "X-Languages";
public const string KeyNoCleanup = "X-NoCleanup";
public const string KeyNoEnrichment = "X-NoEnrichment";
public const string KeyNoDefaults = "X-NoDefaults";
public const string KeyNoResolveLanguages = "X-NoResolveLanguages";
public const string KeyResolveFlow = "X-ResolveFlow";
public const string KeyResolveUrls = "X-ResolveUrls";
@ -32,6 +33,7 @@ public static class ContentHeaders
cache.AddHeader(KeyLanguages);
cache.AddHeader(KeyNoCleanup);
cache.AddHeader(KeyNoEnrichment);
cache.AddHeader(KeyNoDefaults);
cache.AddHeader(KeyNoResolveLanguages);
cache.AddHeader(KeyResolveFlow);
cache.AddHeader(KeyResolveUrls);
@ -63,6 +65,16 @@ public static class ContentHeaders
return builder.WithBoolean(KeyNoEnrichment, value);
}
public static bool NoDefaults(this Context context)
{
return context.AsBoolean(KeyNoDefaults);
}
public static ICloneBuilder WithNoDefaults(this ICloneBuilder builder, bool value = true)
{
return builder.WithBoolean(KeyNoDefaults, value);
}
public static bool Unpublished(this Context context)
{
return context.AsBoolean(KeyUnpublished);

6
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs

@ -211,7 +211,7 @@ public partial class ContentDomainObject : DomainObject<WriteContent>
{
var operation = await ContentOperation.CreateAsync(serviceProvider, c, () => Snapshot);
var newData = operation.GenerateDefaultValues(Snapshot.EditingData.Clone());
var newData = operation.GenerateDefaultValues(Snapshot.EditingData.Clone(), !c.EnrichRequiredFields);
if (!newData.Equals(Snapshot.EditingData))
{
@ -263,7 +263,7 @@ public partial class ContentDomainObject : DomainObject<WriteContent>
c.Data = await operation.ExecuteCreateScriptAsync(c.Data, status, ct);
}
c.Data = operation.GenerateDefaultValues(c.Data);
c.Data = operation.GenerateDefaultValues(c.Data, false);
if (!c.DoNotValidate)
{
@ -336,7 +336,7 @@ public partial class ContentDomainObject : DomainObject<WriteContent>
if (c.EnrichDefaults)
{
newData = operation.GenerateDefaultValues(newData);
newData = operation.GenerateDefaultValues(newData, true);
}
if (newData.Equals(Snapshot.EditingData))

20
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs

@ -47,7 +47,8 @@ public static class ValidationExtensions
operation.ThrowOnErrors();
}
public static async Task ValidateInputAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateInputAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -57,7 +58,8 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static async Task ValidateInputPartialAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateInputPartialAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -67,7 +69,8 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static async Task ValidateContentAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateContentAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -77,7 +80,8 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static async Task ValidateContentAndInputAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateContentAndInputAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -87,18 +91,20 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static ContentData GenerateDefaultValues(this ContentOperation operation, ContentData data)
public static ContentData GenerateDefaultValues(this ContentOperation operation,
ContentData data, bool ignoreRequired)
{
var converter =
new ContentConverter(
operation.Components,
operation.Schema);
converter.Add(new AddDefaultValues(operation.Partition()) { IgnoreRequiredFields = true });
converter.Add(new AddDefaultValues(operation.Partition()) { IgnoreRequiredFields = ignoreRequired });
return converter.Convert(data);
}
public static ContentData InvokeUpdates(this ContentOperation operation, ContentData data, ContentData currentData, bool canUnset)
public static ContentData InvokeUpdates(this ContentOperation operation, ContentData data,
ContentData currentData, bool canUnset)
{
var converter =
new ContentConverter(

9
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs

@ -67,6 +67,7 @@ public sealed class ContentEnricher : IContentEnricher
{
var result = SimpleMapper.Map(content, new EnrichedContent());
// Clone the data to keep the existing value intact (for example when cached in memory).
if (cloneData)
{
using (Telemetry.Activities.StartActivity("ContentEnricher/CloneData"))
@ -86,12 +87,8 @@ public sealed class ContentEnricher : IContentEnricher
{
return schemaCache.GetOrAdd(id, async x =>
{
var schema = await appProvider.GetSchemaAsync(context.App.Id, x, false, ct);
if (schema == null)
{
throw new DomainObjectNotFoundException(x.ToString());
}
var schema = await appProvider.GetSchemaAsync(context.App.Id, x, false, ct)
?? throw new DomainObjectNotFoundException(x.ToString());
var components = await appProvider.GetComponentsAsync(schema, ct);

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs

@ -133,7 +133,7 @@ public sealed class ConvertData : IContentEnricherStep
converter.Add(new ResolveFromPreviousPartitioning(context.App.Languages));
if (!context.IsFrontendClient)
if (!context.IsFrontendClient && !context.NoDefaults())
{
converter.Add(new AddDefaultValues(context.App.PartitionResolver())
{

2
backend/src/Squidex.Domain.Apps.Entities/Jobs/JobWorker.cs

@ -27,7 +27,7 @@ public sealed class JobWorker :
processorFactory = key =>
{
return (JobProcessor)objectFactory(serviceProvider, new object[] { key });
return (JobProcessor)objectFactory(serviceProvider, [key]);
};
}

12
backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs

@ -218,11 +218,11 @@ public sealed class SchemasIndex : ICommandMiddleware, ISchemasIndex
}
// Do not use cancellation here as we already so far.
await schemaCache.AddAsync(new[]
{
await schemaCache.AddAsync(
[
new KeyValuePair<string, object?>(GetCacheKey(schema.AppId.Id, schema.Id), schema),
new KeyValuePair<string, object?>(GetCacheKey(schema.AppId.Id, schema.Name), schema),
}, options.CacheDuration);
], options.CacheDuration);
return schema;
}
@ -235,10 +235,10 @@ public sealed class SchemasIndex : ICommandMiddleware, ISchemasIndex
}
// Do not use cancellation here as we already so far.
return schemaCache.RemoveAsync(new[]
{
return schemaCache.RemoveAsync(
[
GetCacheKey(appId, id),
GetCacheKey(appId, name)
});
]);
}
}

2
backend/src/Squidex.Infrastructure/Migrations/Migrator.cs

@ -86,7 +86,7 @@ public sealed class Migrator
{
while (!await migrationStatus.TryLockAsync(ct))
{
log.LogInformation("Could not acquire lock to start migrating. Tryping again in {time}ms.", LockWaitMs);
log.LogInformation("Could not acquire lock to start migrating. Trying again in {time}ms.", LockWaitMs);
await Task.Delay(LockWaitMs, ct);
}
}

4
backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -464,9 +464,9 @@ public sealed class ContentsController : ApiController
[AcceptHeader.Unpublished]
[AcceptHeader.Languages]
[ApiCosts(1)]
public async Task<IActionResult> PutContentDefaults(string app, string schema, DomainId id)
public async Task<IActionResult> PutContentDefaults(string app, string schema, DomainId id, EnrichContentDefaultsDto request)
{
var command = new EnrichContentDefaults { ContentId = id };
var command = request.ToCommand(id);
var response = await InvokeCommandAsync(command);

5
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs

@ -33,6 +33,11 @@ public sealed class BulkUpdateContentsDto
/// </summary>
public bool DoNotScript { get; set; } = true;
/// <summary>
/// True, to also enrich required fields. Default: false.
/// </summary>
public bool EnrichRequiredFields { get; set; }
/// <summary>
/// True to turn off validation for faster inserts. Default: false.
/// </summary>

29
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/EnrichContentDefaultsDto.cs

@ -0,0 +1,29 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.AspNetCore.Mvc;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Contents.Models;
public class EnrichContentDefaultsDto
{
/// <summary>
/// True, to also enrich required fields. Default: false.
/// </summary>
[FromQuery(Name = "enrichRequiredFields")]
public bool EnrichRequiredFields { get; set; }
public EnrichContentDefaults ToCommand(DomainId id)
{
var command = SimpleMapper.Map(this, new EnrichContentDefaults { ContentId = id });
return command;
}
}

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichForCachingTests.cs

@ -38,6 +38,7 @@ public class EnrichForCachingTests : GivenContext
"X-Languages",
"X-NoCleanup",
"X-NoEnrichment",
"X-NoDefaults",
"X-NoResolveLanguages",
"X-ResolveFlow",
"X-ResolveUrls",

1
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichForCachingTests.cs

@ -39,6 +39,7 @@ public class EnrichForCachingTests : GivenContext
"X-Languages",
"X-NoCleanup",
"X-NoEnrichment",
"X-NoDefaults",
"X-NoResolveLanguages",
"X-ResolveFlow",
"X-ResolveUrls",

3
helm/squidex/templates/service.yaml

@ -11,5 +11,8 @@ spec:
targetPort: http
protocol: TCP
name: http
{{- if and (eq (lower .Values.service.type) "nodeport") .Values.service.nodePort }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
selector:
{{- include "squidex.selectors" . | indent 4 }}

3
helm/squidex/values.yaml

@ -9,6 +9,9 @@ service:
## @param service.port Kubernetes Service port
##
port: 80
## @param service.port Kubernetes Service port
##
nodePort: null
deployment:
## @param deployment.replicaCount Number of instances.
##

3
tools/.editorconfig

@ -30,6 +30,9 @@ dotnet_diagnostic.IDE0305.severity = none
# CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1707.severity = none
# CA1861: Avoid constant arrays as arguments
dotnet_diagnostic.CA1861.severity = none
# CA2016: Forward the 'CancellationToken' parameter to methods
dotnet_diagnostic.CA2016.severity = none

2
tools/TestSuite/TestSuite.ApiTests/AssetFoldersTests.cs

@ -145,7 +145,7 @@ public class AssetFoldersTests : IClassFixture<CreatedAppFixture>
// STEP 3: Query by nested id.
var folders2 = await _.Client.Assets.GetAssetFoldersAsync(folder1.Id);
Assert.Equal(new[] { folder2.Id }, folders2.Items.Select(x => x.Id));
Assert.Equal([folder2.Id], folders2.Items.Select(x => x.Id));
// STEP 3: Query all

2
tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs

@ -633,7 +633,7 @@ public class ContentQueryTests : IClassFixture<ContentQueryFixture>
}
};
var results = await _.Client.SharedDynamicContents.GraphQlAsync<QueryResult>(new[] { query1, query2 });
var results = await _.Client.SharedDynamicContents.GraphQlAsync<QueryResult>([query1, query2]);
var items1 = results.ElementAt(0).Data.Items;
var items2 = results.ElementAt(1).Data.Items;

155
tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs

@ -941,7 +941,7 @@ public class ContentUpdateTests : IClassFixture<ContentFixture>
[InlineData(ContentStrategies.EnrichDefaults.Bulk)]
[InlineData(ContentStrategies.EnrichDefaults.BulkWithSchema)]
[InlineData(ContentStrategies.EnrichDefaults.BulkShared)]
public async Task Should_enrich_defaults(ContentStrategies.EnrichDefaults strategy)
public async Task Should_enrich_default_fields(ContentStrategies.EnrichDefaults strategy)
{
var schemaName = $"schema-{Guid.NewGuid()}";
@ -949,14 +949,6 @@ public class ContentUpdateTests : IClassFixture<ContentFixture>
var schemaRequest = new CreateSchemaDto
{
Name = schemaName,
Fields =
[
new UpsertSchemaFieldDto
{
Name = TestEntityData.NumberField,
Properties = new NumberFieldPropertiesDto()
},
],
IsPublished = true
};
@ -968,30 +960,161 @@ public class ContentUpdateTests : IClassFixture<ContentFixture>
// STEP 1: Create a new item.
var content_0 = await contents.CreateAsync([], ContentCreateOptions.AsPublish);
Assert.Null(content_0.Data.GetValueOrDefault(TestEntityData.StringField));
Assert.Null(content_0.Data.GetValueOrDefault("fieldDefault"));
// STEP 2: Add required field.
// STEP 2: Add new fields.
var fieldRequest = new AddFieldDto
{
Name = TestEntityData.StringField,
Name = "fieldDefault",
Properties = new StringFieldPropertiesDto
{
DefaultValue = "Hello Squidex",
IsRequired = false
}
};
await _.Client.Schemas.PostFieldAsync(schemaName, fieldRequest);
// STEP 3: Create required field.
await _.Client.EnrichDefaultsAsync(content_0, content_0.Data, strategy, false);
// STEP 4: Get content.
var context = QueryContext.Default.WithHeaderHandler(request =>
{
request.Headers.TryAddWithoutValidation("X-NoDefaults", "1");
});
var content_1 = await contents.GetAsync(content_0.Id, context);
Assert.Equal("Hello Squidex", content_1.Data["fieldDefault"]!["iv"]!.ToString());
}
[Theory]
[InlineData(ContentStrategies.EnrichDefaults.Normal)]
[InlineData(ContentStrategies.EnrichDefaults.Bulk)]
[InlineData(ContentStrategies.EnrichDefaults.BulkWithSchema)]
[InlineData(ContentStrategies.EnrichDefaults.BulkShared)]
public async Task Should_enrich_non_required_default_fields(ContentStrategies.EnrichDefaults strategy)
{
var schemaName = $"schema-{Guid.NewGuid()}";
// STEP 0: Create initial schema.
var schemaRequest = new CreateSchemaDto
{
Name = schemaName,
IsPublished = true
};
await _.Client.Schemas.PostSchemaAsync(schemaRequest);
var contents = _.Client.DynamicContents(schemaName);
// STEP 1: Create a new item.
var content_0 = await contents.CreateAsync([], ContentCreateOptions.AsPublish);
Assert.Null(content_0.Data.GetValueOrDefault("fieldDefault"));
Assert.Null(content_0.Data.GetValueOrDefault("fieldRequired"));
// STEP 2: Add new fields.
var fieldRequest1 = new AddFieldDto
{
Name = "fieldDefault",
Properties = new StringFieldPropertiesDto
{
DefaultValue = "Hello Squidex",
IsRequired = false
}
};
var fieldRequest2 = new AddFieldDto
{
Name = "fieldRequired",
Properties = new StringFieldPropertiesDto
{
DefaultValue = "Hello Squidex"
DefaultValue = "Hello Required",
IsRequired = true
}
};
await _.Client.Schemas.PostFieldAsync(schemaName, fieldRequest1);
await _.Client.Schemas.PostFieldAsync(schemaName, fieldRequest2);
// STEP 3: Create required field.
await _.Client.EnrichDefaultsAsync(content_0, content_0.Data, strategy, false);
// STEP 4: Get content.
var context = QueryContext.Default.WithHeaderHandler(request =>
{
request.Headers.TryAddWithoutValidation("X-NoDefaults", "1");
});
var content_1 = await contents.GetAsync(content_0.Id, context);
Assert.Equal("Hello Squidex", content_1.Data["fieldDefault"]!["iv"]!.ToString());
Assert.Null(content_0.Data.GetValueOrDefault("fieldRequired"));
}
[Theory]
[InlineData(ContentStrategies.EnrichDefaults.Normal)]
[InlineData(ContentStrategies.EnrichDefaults.Bulk)]
[InlineData(ContentStrategies.EnrichDefaults.BulkWithSchema)]
[InlineData(ContentStrategies.EnrichDefaults.BulkShared)]
public async Task Should_enrich_required_default_field_if_flag_is_true(ContentStrategies.EnrichDefaults strategy)
{
var schemaName = $"schema-{Guid.NewGuid()}";
// STEP 0: Create initial schema.
var schemaRequest = new CreateSchemaDto
{
Name = schemaName,
IsPublished = true
};
await _.Client.Schemas.PostSchemaAsync(schemaRequest);
var contents = _.Client.DynamicContents(schemaName);
// STEP 1: Create a new item.
var content_0 = await contents.CreateAsync([], ContentCreateOptions.AsPublish);
Assert.Null(content_0.Data.GetValueOrDefault("fieldDefault"));
Assert.Null(content_0.Data.GetValueOrDefault("fieldRequired"));
// STEP 2: Add new field.
var fieldRequest = new AddFieldDto
{
Name = "fieldRequired",
Properties = new StringFieldPropertiesDto
{
DefaultValue = "Hello Required",
IsRequired = true
}
};
await _.Client.Schemas.PostFieldAsync(schemaName, fieldRequest);
// STEP 3: Create required field.
await _.Client.EnrichDefaultsAsync(content_0, content_0.Data, strategy);
await _.Client.EnrichDefaultsAsync(content_0, content_0.Data, strategy, true);
// STEP 4: Get content.
var content_1 = await contents.GetAsync(content_0.Id);
var context = QueryContext.Default.WithHeaderHandler(request =>
{
request.Headers.TryAddWithoutValidation("X-NoDefaults", "1");
});
var content_1 = await contents.GetAsync(content_0.Id, context);
Assert.Equal("Hello Squidex", content_1.Data[TestEntityData.StringField]!["iv"]!.ToString());
Assert.Equal("Hello Required", content_1.Data["fieldRequired"]!["iv"]!.ToString());
}
[Fact]

15
tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

@ -239,7 +239,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x.Data.Name)
.Order();
Assert.Equal(new[] { "Leipzig", "Munich" }, cityNames);
Assert.Equal(["Leipzig", "Munich"], cityNames);
}
[Fact]
@ -276,7 +276,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]!["name"]!.Value<string>())
.Order();
Assert.Equal(new[] { "Leipzig", "Munich" }, cityNames);
Assert.Equal(["Leipzig", "Munich"], cityNames);
}
[Fact]
@ -313,7 +313,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]!["name"]!.Value<string>())
.Order();
Assert.Equal(new[] { "Leipzig" }, cityNames);
Assert.Equal(["Leipzig"], cityNames);
}
[Fact]
@ -341,7 +341,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]!["name"]!.Value<string>())
.Order();
Assert.Equal(new[] { "Bavaria", "Saxony" }, stateNames);
Assert.Equal(["Bavaria", "Saxony"], stateNames);
}
[Fact]
@ -369,7 +369,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x!["data"]!["name"]!.Value<string>())
.Order();
Assert.Equal(new[] { "Saxony" }, stateNames);
Assert.Equal(["Saxony"], stateNames);
}
[Fact]
@ -392,7 +392,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data__dynamic"]!["name"]!["iv"]!.Value<string>())
.Order();
Assert.Equal(new[] { "Leipzig", "Munich" }, cityNames);
Assert.Equal(["Leipzig", "Munich"], cityNames);
}
[Fact]
@ -458,7 +458,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]!["name"]!.Value<string>())
.Order();
Assert.Equal(new[] { "Bavaria", "Leipzig", "Munich", "Saxony" }, names);
Assert.Equal(["Bavaria", "Leipzig", "Munich", "Saxony"], names);
}
[Fact]
@ -523,6 +523,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
"X-Flatten",
"X-Languages",
"X-NoCleanup",
"X-NoDefaults",
"X-NoEnrichment",
"X-NoResolveLanguages",
"X-ResolveFlow",

20
tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj

@ -8,20 +8,20 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="GraphQL.Client" Version="6.0.5" />
<PackageReference Include="GraphQL.Client.Serializer.Newtonsoft" Version="6.0.5" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.150">
<PackageReference Include="GraphQL.Client" Version="6.1.0" />
<PackageReference Include="GraphQL.Client.Serializer.Newtonsoft" Version="6.1.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NSwag.Core" Version="14.0.7" />
<PackageReference Include="Squidex.Assets" Version="6.6.4" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="6.6.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="NSwag.Core" Version="14.1.0" />
<PackageReference Include="Squidex.Assets" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="6.18.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="Verify.Xunit" Version="24.2.0" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PackageReference Include="Verify.Xunit" Version="26.2.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

8
tools/TestSuite/TestSuite.LoadTests/TestSuite.LoadTests.csproj

@ -7,14 +7,14 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.150">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

2
tools/TestSuite/TestSuite.Shared/ClientExtensions.cs

@ -338,7 +338,7 @@ public static class ClientExtensions
return temp;
}
public static async Task UploadInChunksAsync(this IAssetsClient client, ProgressHandler progress, FileParameter fileParameter, string? id = null)
public static async Task UploadInChunksAsync(this IAssetsClient client, ProgressHandler progress, FileParameter fileParameter, string? id = null)
{
var pausingStream = new PauseStream(fileParameter.Data, 0.25);
var pausingFile = new FileParameter(pausingStream, fileParameter.FileName, fileParameter.ContentType)

49
tools/TestSuite/TestSuite.Shared/ContentStrategies.cs

@ -21,7 +21,8 @@ public static partial class ContentStrategies
BulkPermanent
}
public static Task DeleteAsync(this ISquidexClient client, ContentBase content, Deletion strategy)
public static Task DeleteAsync(this ISquidexClient client, ContentBase content,
Deletion strategy)
{
IContentsClient<MyContent, object> GetClient()
{
@ -75,7 +76,8 @@ public static partial class ContentStrategies
BulkShared
}
public static Task UpdateAsync(this ISquidexClient client, ContentBase content, object data, Update strategy)
public static Task UpdateAsync(this ISquidexClient client, ContentBase content, object data,
Update strategy)
{
IContentsClient<MyContent, object> GetClient()
{
@ -157,7 +159,8 @@ public static partial class ContentStrategies
BulkShared
}
public static Task PatchAsync(this ISquidexClient client, ContentBase content, object data, Patch strategy)
public static Task PatchAsync(this ISquidexClient client, ContentBase content, object data,
Patch strategy)
{
IContentsClient<MyContent, object> GetClient()
{
@ -242,7 +245,8 @@ public static partial class ContentStrategies
UpdateBulk
}
public static Task EnrichDefaultsAsync(this ISquidexClient client, ContentBase content, object data, EnrichDefaults strategy)
public static Task EnrichDefaultsAsync(this ISquidexClient client, ContentBase content, object data,
EnrichDefaults strategy, bool requiredFields)
{
IContentsClient<MyContent, object> GetClient()
{
@ -252,11 +256,27 @@ public static partial class ContentStrategies
switch (strategy)
{
case EnrichDefaults.Normal:
return GetClient().EnrichDefaultsAsync(content.Id);
var createOptions = new ContentEnrichDefaultsOptions
{
EnrichRequiredFields = requiredFields
};
return GetClient().EnrichDefaultsAsync(content.Id, createOptions);
case EnrichDefaults.Update:
return GetClient().UpdateAsync(content.Id, data, ContentUpdateOptions.AsEnrichDefaults);
var updateOptions = new ContentUpdateOptions
{
EnrichDefaults = true
};
return GetClient().UpdateAsync(content.Id, data, updateOptions);
case EnrichDefaults.Upsert:
return GetClient().UpsertAsync(content.Id, data, ContentUpsertOptions.AsEnrichDefaults);
var upsertOptions = new ContentUpsertOptions
{
EnrichDefaults = true,
EnrichRequiredFields = requiredFields
};
return GetClient().UpsertAsync(content.Id, data, upsertOptions);
case EnrichDefaults.Bulk:
return GetClient().BulkUpdateAsync(new BulkUpdate
{
@ -267,7 +287,8 @@ public static partial class ContentStrategies
Type = BulkUpdateType.EnrichDefaults,
Id = content.Id
},
]
],
EnrichRequiredFields = requiredFields
});
case EnrichDefaults.UpdateBulk:
return GetClient().BulkUpdateAsync(new BulkUpdate
@ -281,7 +302,8 @@ public static partial class ContentStrategies
Data = data,
EnrichDefaults = true,
},
]
],
EnrichRequiredFields = requiredFields
});
case EnrichDefaults.UpsertBulk:
return GetClient().BulkUpdateAsync(new BulkUpdate
@ -295,7 +317,8 @@ public static partial class ContentStrategies
Data = data,
EnrichDefaults = true,
},
]
],
EnrichRequiredFields = requiredFields
});
case EnrichDefaults.BulkWithSchema:
return GetClient().BulkUpdateAsync(new BulkUpdate
@ -308,7 +331,8 @@ public static partial class ContentStrategies
Id = content.Id,
Schema = content.SchemaName
},
]
],
EnrichRequiredFields = requiredFields
});
case EnrichDefaults.BulkShared:
return GetSharedClient(client).BulkUpdateAsync(new BulkUpdate
@ -321,7 +345,8 @@ public static partial class ContentStrategies
Id = content.Id,
Schema = content.SchemaName
},
]
],
EnrichRequiredFields = requiredFields
});
default:
return Task.CompletedTask;

14
tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj

@ -7,22 +7,22 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.150">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets" Version="6.6.4" />
<PackageReference Include="Squidex.ClientLibrary" Version="19.2.0" />
<PackageReference Include="Squidex.ClientLibrary.ServiceExtensions" Version="19.2.0" />
<PackageReference Include="Squidex.Assets" Version="6.18.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="19.7.0" />
<PackageReference Include="Squidex.ClientLibrary.ServiceExtensions" Version="19.7.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="Verify" Version="24.2.0" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="Verify" Version="26.2.0" />
<PackageReference Include="xunit" Version="2.9.0" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />

Loading…
Cancel
Save