Browse Source

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

# Conflicts:
#	backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentsBulkUpdateCommandMiddleware.cs
#	backend/src/Squidex.Infrastructure/Diagnostics/Diagnoser.cs
#	backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs
pull/933/head
Sebastian 4 years ago
parent
commit
d1eabf8d7b
  1. 2
      backend/extensions/Squidex.Extensions/APM/Otlp/OtlpPlugin.cs
  2. 8
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs
  3. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  4. 2
      backend/src/Squidex.Domain.Apps.Entities/Comments/Commands/_CommentsCommand.cs
  5. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContents.cs
  6. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentsBulkUpdateCommandMiddleware.cs
  7. 3
      backend/src/Squidex.Infrastructure/Diagnostics/Diagnoser.cs
  8. 12
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  9. 2
      backend/src/Squidex/Areas/Api/Config/OpenApi/XmlTagProcessor.cs
  10. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  11. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs
  12. 8
      backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasOpenApiGenerator.cs
  13. 24
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs
  14. 21
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs
  15. 9
      backend/src/Squidex/Config/Domain/TelemetryServices.cs
  16. 2
      backend/src/Squidex/Config/Messaging/MessagingServices.cs
  17. 24
      backend/src/Squidex/Squidex.csproj
  18. 5
      backend/src/Squidex/appsettings.json
  19. 3
      backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs
  20. 7
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppState.json
  21. 40
      backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs
  22. 82
      backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  23. 6
      backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs
  24. 2
      backend/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj
  25. 5
      backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs
  26. 4
      backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj
  27. 2
      frontend/src/app/features/settings/pages/workflows/workflow-step.component.html

2
backend/extensions/Squidex.Extensions/APM/Otlp/OtlpPlugin.cs

@ -32,6 +32,8 @@ namespace Squidex.Extensions.APM.Otlp
builder.AddOtlpExporter(options => builder.AddOtlpExporter(options =>
{ {
config.GetSection("logging:otlp").Bind(options); config.GetSection("logging:otlp").Bind(options);
}); });
} }
} }

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

@ -16,11 +16,13 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
{ {
public Dictionary<Status, WorkflowTransitionSurrogate> Transitions { get; set; } public Dictionary<Status, WorkflowTransitionSurrogate> Transitions { get; set; }
[JsonPropertyName("noUpdateRules")]
public NoUpdate? NoUpdate { get; set; }
[JsonPropertyName("noUpdate")] [JsonPropertyName("noUpdate")]
public bool NoUpdateFlag { get; set; } public bool NoUpdateFlag { get; set; }
[JsonPropertyName("noUpdateRules")] public bool Validate { get; set; }
public NoUpdate? NoUpdate { get; set; }
public string? Color { get; set; } public string? Color { get; set; }
@ -52,7 +54,7 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
x => x.Key, x => x.Key,
x => x.Value.ToSource()); x => x.Value.ToSource());
return new WorkflowStep(transitions, Color, noUpdate); return new WorkflowStep(transitions, Color, noUpdate, Validate);
} }
} }
} }

2
backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -28,7 +28,7 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="NJsonSchema" Version="10.7.2" /> <PackageReference Include="NJsonSchema" Version="10.7.2" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="4.12.0" /> <PackageReference Include="Squidex.Messaging.Subscriptions" Version="4.13.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="6.0.0" /> <PackageReference Include="System.Collections.Immutable" Version="6.0.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" /> <PackageReference Include="System.Linq.Async" Version="6.0.1" />

2
backend/src/Squidex.Domain.Apps.Entities/Comments/Commands/_CommentsCommand.cs

@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Comments.Commands
public abstract class CommentsCommand : CommentsCommandBase public abstract class CommentsCommand : CommentsCommandBase
{ {
public static readonly NamedId<DomainId> NoApp = NamedId.Of(DomainId.NewGuid(), "none"); public static readonly NamedId<DomainId> NoApp = NamedId.Of(DomainId.Empty, "none");
public DomainId CommentsId { get; set; } public DomainId CommentsId { get; set; }

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

@ -11,6 +11,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands
{ {
public sealed class BulkUpdateContents : SquidexCommand, IAppCommand, ISchemaCommand public sealed class BulkUpdateContents : SquidexCommand, IAppCommand, ISchemaCommand
{ {
public static readonly NamedId<DomainId> NoSchema = NamedId.Of(DomainId.Empty, "none");
public NamedId<DomainId> AppId { get; set; } public NamedId<DomainId> AppId { get; set; }
public NamedId<DomainId> SchemaId { get; set; } public NamedId<DomainId> SchemaId { get; set; }

2
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentsBulkUpdateCommandMiddleware.cs

@ -175,7 +175,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
} }
// The bulk command can be invoke in a schema controller or without a schema controller, therefore the name might be null. // The bulk command can be invoke in a schema controller or without a schema controller, therefore the name might be null.
if (task.SchemaId == null) if (task.SchemaId == null || task.SchemaId.Id == default)
{ {
throw new DomainObjectNotFoundException("undefined"); throw new DomainObjectNotFoundException("undefined");
} }

3
backend/src/Squidex.Infrastructure/Diagnostics/Diagnoser.cs

@ -107,8 +107,7 @@ namespace Squidex.Infrastructure.Diagnostics
return false; return false;
} }
using var cts = new CancellationTokenSource(DefaultTimeout); using var combined = CancellationTokenSource.CreateLinkedTokenSource(ct);
using var combined = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ct);
// Enforce a hard timeout. // Enforce a hard timeout.
combined.CancelAfter(DefaultTimeout); combined.CancelAfter(DefaultTimeout);

12
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -24,12 +24,12 @@
<PackageReference Include="NodaTime" Version="3.1.2" /> <PackageReference Include="NodaTime" Version="3.1.2" />
<PackageReference Include="OpenTelemetry.Api" Version="1.3.0" /> <PackageReference Include="OpenTelemetry.Api" Version="1.3.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets" Version="4.12.0" /> <PackageReference Include="Squidex.Assets" Version="4.13.0" />
<PackageReference Include="Squidex.Caching" Version="4.12.0" /> <PackageReference Include="Squidex.Caching" Version="4.13.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="4.12.0" /> <PackageReference Include="Squidex.Hosting.Abstractions" Version="4.13.0" />
<PackageReference Include="Squidex.Log" Version="4.12.0" /> <PackageReference Include="Squidex.Log" Version="4.13.0" />
<PackageReference Include="Squidex.Messaging" Version="4.12.0" /> <PackageReference Include="Squidex.Messaging" Version="4.13.0" />
<PackageReference Include="Squidex.Text" Version="4.12.0" /> <PackageReference Include="Squidex.Text" Version="4.13.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="6.0.0" /> <PackageReference Include="System.Collections.Immutable" Version="6.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />

2
backend/src/Squidex/Areas/Api/Config/OpenApi/XmlTagProcessor.cs

@ -33,7 +33,7 @@ namespace Squidex.Areas.Api.Config.OpenApi
if (description != null) if (description != null)
{ {
tag.Description ??= string.Empty; tag.Description ??= string.Empty;
if (!tag.Description.Contains(description, StringComparison.Ordinal)) if (!tag.Description.Contains(description, StringComparison.Ordinal))
{ {

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

@ -331,7 +331,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(5)] [ApiCosts(5)]
public async Task<IActionResult> BulkUpdateContents(string app, string schema, [FromBody] BulkUpdateContentsDto request) public async Task<IActionResult> BulkUpdateContents(string app, string schema, [FromBody] BulkUpdateContentsDto request)
{ {
var command = request.ToCommand(); var command = request.ToCommand(false);
var context = await CommandBus.PublishAsync(command, HttpContext.RequestAborted); var context = await CommandBus.PublishAsync(command, HttpContext.RequestAborted);

2
backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs

@ -135,7 +135,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(5)] [ApiCosts(5)]
public async Task<IActionResult> BulkUpdateContents(string app, string schema, [FromBody] BulkUpdateContentsDto request) public async Task<IActionResult> BulkUpdateContents(string app, string schema, [FromBody] BulkUpdateContentsDto request)
{ {
var command = request.ToCommand(); var command = request.ToCommand(true);
var context = await CommandBus.PublishAsync(command, HttpContext.RequestAborted); var context = await CommandBus.PublishAsync(command, HttpContext.RequestAborted);

8
backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasOpenApiGenerator.cs

@ -100,6 +100,14 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator
.HasQuery("ids", JsonObjectType.String, "Comma-separated list of content IDs.") .HasQuery("ids", JsonObjectType.String, "Comma-separated list of content IDs.")
.Responds(200, "Content items retrieved.", builder.ContentsSchema) .Responds(200, "Content items retrieved.", builder.ContentsSchema)
.Responds(400, "Query not valid."); .Responds(400, "Query not valid.");
builder.AddOperation(OpenApiOperationMethod.Post, "/bulk")
.RequirePermission(PermissionIds.AppContentsReadOwn)
.Operation("Bulk")
.OperationSummary("Bulk update content items across all schemas.")
.HasBody("request", builder.Parent.BulkRequestSchema, null)
.Responds(200, "Contents created, update or delete.", builder.Parent.BulkResponseSchema)
.Responds(400, "Contents request not valid.");
} }
private static void GenerateSchemaOperations(OperationsBuilder builder) private static void GenerateSchemaOperations(OperationsBuilder builder)

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

@ -51,25 +51,29 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// </summary> /// </summary>
public bool OptimizeValidation { get; set; } = true; public bool OptimizeValidation { get; set; } = true;
public BulkUpdateContents ToCommand() public BulkUpdateContents ToCommand(bool setSchema)
{ {
var result = SimpleMapper.Map(this, new BulkUpdateContents()); var result = SimpleMapper.Map(this, new BulkUpdateContents());
result.Jobs = Jobs?.Select(x => x.ToJob())?.ToArray(); result.Jobs = Jobs?.Select(x =>
{
var job = x.ToJob();
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
if (result.Jobs != null && Publish) if (Publish)
{
foreach (var job in result.Jobs)
{ {
if (job != null) job.Status = Status.Published;
{
job.Status = Status.Published;
}
} }
}
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete
return job;
}).ToArray();
if (setSchema)
{
result.SchemaId = BulkUpdateContents.NoSchema;
}
return result; return result;
} }
} }

21
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs

@ -40,21 +40,24 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
{ {
var result = SimpleMapper.Map(this, new BulkUpdateContents()); var result = SimpleMapper.Map(this, new BulkUpdateContents());
result.Jobs = Datas?.Select(x => new BulkUpdateJob { Type = BulkUpdateContentType.Create, Data = x }).ToArray(); result.Jobs = Datas?.Select(x =>
{
var job = new BulkUpdateJob
{
Type = BulkUpdateContentType.Create,
Data = x
};
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
if (result.Jobs != null && Publish) if (Publish)
{
foreach (var job in result.Jobs)
{ {
if (job != null) job.Status = Status.Published;
{
job.Status = Status.Published;
}
} }
}
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete
return job;
}).ToArray();
return result; return result;
} }
} }

9
backend/src/Squidex/Config/Domain/TelemetryServices.cs

@ -35,6 +35,15 @@ namespace Squidex.Config.Domain
builder.AddHttpClientInstrumentation(); builder.AddHttpClientInstrumentation();
builder.AddMongoDBInstrumentation(); builder.AddMongoDBInstrumentation();
var sampling = config.GetValue<double>("logging:otlp:sampling");
if (sampling > 0 && sampling < 1)
{
builder.SetSampler(
new ParentBasedSampler(
new TraceIdRatioBasedSampler(sampling)));
}
foreach (var configurator in serviceProvider.GetRequiredService<IEnumerable<ITelemetryConfigurator>>()) foreach (var configurator in serviceProvider.GetRequiredService<IEnumerable<ITelemetryConfigurator>>())
{ {
configurator.Configure(builder); configurator.Configure(builder);

2
backend/src/Squidex/Config/Messaging/MessagingServices.cs

@ -63,7 +63,7 @@ namespace Squidex.Config.Messaging
} }
services.AddSingleton<IMessagingSerializer>(c => services.AddSingleton<IMessagingSerializer>(c =>
new SystemTextJsonTransportSerializer(c.GetRequiredService<JsonSerializerOptions>())); new SystemTextJsonMessagingSerializer(c.GetRequiredService<JsonSerializerOptions>()));
services.AddSingletonAs<SubscriptionPublisher>() services.AddSingletonAs<SubscriptionPublisher>()
.As<IEventConsumer>(); .As<IEventConsumer>();

24
backend/src/Squidex/Squidex.csproj

@ -69,19 +69,19 @@
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc7" /> <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc7" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="ReportGenerator" Version="5.1.9" PrivateAssets="all" /> <PackageReference Include="ReportGenerator" Version="5.1.9" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets.Azure" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.Azure" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.GoogleCloud" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.GoogleCloud" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.FTP" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.FTP" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.ImageMagick" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.ImageMagick" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.ImageSharp" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.Mongo" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.Mongo" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.S3" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.S3" Version="4.13.0" />
<PackageReference Include="Squidex.Assets.TusAdapter" Version="4.12.0" /> <PackageReference Include="Squidex.Assets.TusAdapter" Version="4.13.0" />
<PackageReference Include="Squidex.Caching.Orleans" Version="1.9.0" /> <PackageReference Include="Squidex.Caching.Orleans" Version="1.9.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="9.2.0" /> <PackageReference Include="Squidex.ClientLibrary" Version="10.2.0" />
<PackageReference Include="Squidex.Hosting" Version="4.12.0" /> <PackageReference Include="Squidex.Hosting" Version="4.13.0" />
<PackageReference Include="Squidex.Messaging.All" Version="4.12.0" /> <PackageReference Include="Squidex.Messaging.All" Version="4.13.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="4.12.0" /> <PackageReference Include="Squidex.Messaging.Subscriptions" Version="4.13.0" />
<PackageReference Include="Squidex.Namotion.Reflection" Version="2.0.10" /> <PackageReference Include="Squidex.Namotion.Reflection" Version="2.0.10" />
<PackageReference Include="Squidex.OpenIddict.MongoDb" Version="4.0.1-dev" /> <PackageReference Include="Squidex.OpenIddict.MongoDb" Version="4.0.1-dev" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />

5
backend/src/Squidex/appsettings.json

@ -337,7 +337,10 @@
"enabled": false, "enabled": false,
// The endpoint to the agent. // The endpoint to the agent.
"endpoint": "" "endpoint": "",
// The sample rate as double. 0.5 writes every second trace.
"sampling": 1.0
}, },
"applicationInsights": { "applicationInsights": {

3
backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs

@ -29,7 +29,8 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
[Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2") [Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2")
}.ToReadonlyDictionary(), }.ToReadonlyDictionary(),
"#00ff00", "#00ff00",
NoUpdate.When("Expression", "Role1", "Role2")) NoUpdate.When("Expression", "Role1", "Role2"),
true)
}.ToReadonlyDictionary(), }.ToReadonlyDictionary(),
ReadonlyList.Create(DomainId.NewGuid()), "MyName"); ReadonlyList.Create(DomainId.NewGuid()), "MyName");

7
backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppState.json

@ -87,10 +87,9 @@
} }
}, },
"noUpdateRules": {},
"noUpdate": false, "noUpdate": false,
"noUpdateRules": { "validate": true,
},
"color": "#eb3142" "color": "#eb3142"
}, },
"Draft": { "Draft": {
@ -103,6 +102,7 @@
} }
}, },
"noUpdate": false, "noUpdate": false,
"validate": false,
"color": "#8091a5" "color": "#8091a5"
}, },
"Published": { "Published": {
@ -115,6 +115,7 @@
} }
}, },
"noUpdate": false, "noUpdate": false,
"validate": false,
"color": "#4bb958" "color": "#4bb958"
} }
}, },

40
backend/tools/TestSuite/TestSuite.ApiTests/ContentQueryTests.cs

@ -46,9 +46,28 @@ namespace TestSuite.ApiTests
{ {
var q = new ContentQuery { OrderBy = "data/number/iv asc" }; var q = new ContentQuery { OrderBy = "data/number/iv asc" };
var items = await _.Contents.GetAsync(q); var itemsByQ = await _.Contents.GetAsync(q);
var itemsIds = itemsByQ.Items.Take(3).Select(x => x.Id).ToHashSet();
var itemsById = await _.Contents.GetAsync(new ContentQuery { Ids = itemsIds });
Assert.Equal(3, itemsById.Items.Count);
Assert.Equal(3, itemsById.Total);
foreach (var item in itemsById.Items)
{
Assert.Equal(_.AppName, item.AppName);
Assert.Equal(_.SchemaName, item.SchemaName);
}
}
var itemsById = await _.Contents.GetAsync(new HashSet<string>(items.Items.Take(3).Select(x => x.Id))); [Fact]
public async Task Should_query_by_ids_across_schemas()
{
var q = new ContentQuery { OrderBy = "data/number/iv asc" };
var itemsByQ = await _.Contents.GetAsync(q);
var itemsIds = itemsByQ.Items.Take(3).Select(x => x.Id).ToHashSet();
var itemsById = await _.SharedContents.GetAsync(itemsIds);
Assert.Equal(3, itemsById.Items.Count); Assert.Equal(3, itemsById.Items.Count);
Assert.Equal(3, itemsById.Total); Assert.Equal(3, itemsById.Total);
@ -418,7 +437,7 @@ namespace TestSuite.ApiTests
}" }"
}; };
var result = await _.Contents.GraphQlAsync<JObject>(query); var result = await _.SharedContents.GraphQlAsync<JObject>(query);
var value = result["createMyReadsContent"]["data"]["number"]["iv"].Value<int>(); var value = result["createMyReadsContent"]["data"]["number"]["iv"].Value<int>();
@ -489,7 +508,7 @@ namespace TestSuite.ApiTests
} }
}; };
var result = await _.Contents.GraphQlAsync<JObject>(query); var result = await _.SharedContents.GraphQlAsync<JObject>(query);
var value = result["createMyReadsContent"]["data"]["number"]["iv"].Value<int>(); var value = result["createMyReadsContent"]["data"]["number"]["iv"].Value<int>();
@ -537,7 +556,7 @@ namespace TestSuite.ApiTests
} }
}; };
var results = await _.Contents.GraphQlAsync<QueryResult>(new[] { query1, query2 }); var results = await _.SharedContents.GraphQlAsync<QueryResult>(new[] { query1, query2 });
var items1 = results.ElementAt(0).Data.Items; var items1 = results.ElementAt(0).Data.Items;
var items2 = results.ElementAt(1).Data.Items; var items2 = results.ElementAt(1).Data.Items;
@ -568,8 +587,7 @@ namespace TestSuite.ApiTests
} }
}; };
var result = await _.Contents.GraphQlAsync<QueryResult>(query); var result = await _.SharedContents.GraphQlAsync<QueryResult>(query);
var items = result.Items; var items = result.Items;
Assert.Equal(items.Select(x => x.Data.Number).ToArray(), new[] { 4, 5, 6 }); Assert.Equal(items.Select(x => x.Data.Number).ToArray(), new[] { 4, 5, 6 });
@ -597,8 +615,7 @@ namespace TestSuite.ApiTests
} }
}; };
var result = await _.Contents.GraphQlGetAsync<QueryResult>(query); var result = await _.SharedContents.GraphQlGetAsync<QueryResult>(query);
var items = result.Items; var items = result.Items;
Assert.Equal(items.Select(x => x.Data.Number).ToArray(), new[] { 4, 5, 6 }); Assert.Equal(items.Select(x => x.Data.Number).ToArray(), new[] { 4, 5, 6 });
@ -622,8 +639,7 @@ namespace TestSuite.ApiTests
}" }"
}; };
var result = await _.Contents.GraphQlAsync<JObject>(query); var result = await _.SharedContents.GraphQlAsync<JObject>(query);
var items = result["queryMyReadsContents"]; var items = result["queryMyReadsContents"];
Assert.Equal(items.Select(x => x["data"]["number"]["iv"].Value<int>()).ToArray(), new[] { 4, 5, 6 }); Assert.Equal(items.Select(x => x["data"]["number"]["iv"].Value<int>()).ToArray(), new[] { 4, 5, 6 });
@ -651,7 +667,7 @@ namespace TestSuite.ApiTests
} }
}; };
await _.Contents.GraphQlAsync<QueryResult>(query); await _.SharedContents.GraphQlAsync<QueryResult>(query);
} }
[Fact] [Fact]

82
backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs

@ -915,6 +915,88 @@ namespace TestSuite.ApiTests
} }
} }
[Fact]
public async Task Should_update_content_with_bulk_and_shared_client()
{
TestEntity content = null;
try
{
var schemaName = $"schema-{Guid.NewGuid()}";
// STEP 0: Create dummy schema.
var createSchema = new CreateSchemaDto
{
Name = schemaName,
// Publish it to avoid validations issues.
IsPublished = true
};
await _.Schemas.PostSchemaAsync(_.AppName, createSchema);
// STEP 1: Create a new item.
content = await _.Contents.CreateAsync(new TestEntityData
{
String = "test"
}, ContentCreateOptions.AsPublish);
// STEP 2: Patch an item.
await _.SharedContents.BulkUpdateAsync(new BulkUpdate
{
Jobs = new List<BulkUpdateJob>
{
new BulkUpdateJob
{
Id = content.Id,
Data = new
{
number = new
{
iv = 1
}
},
Schema = _.SchemaName
}
}
});
// STEP 3: Update the item and ensure that the data has changed.
await _.SharedContents.BulkUpdateAsync(new BulkUpdate
{
Jobs = new List<BulkUpdateJob>
{
new BulkUpdateJob
{
Id = content.Id,
Data = new
{
number = new
{
iv = 2
}
},
Schema = _.SchemaName
}
}
});
var updated = await _.Contents.GetAsync(content.Id);
Assert.Equal(2, updated.Data.Number);
}
finally
{
if (content != null)
{
await _.Contents.DeleteAsync(content.Id);
}
}
}
[Fact] [Fact]
public async Task Should_create_draft_version() public async Task Should_create_draft_version()
{ {

6
backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

@ -92,7 +92,7 @@ namespace TestSuite.ApiTests
}".Replace("<ID>", content_0.Id, StringComparison.Ordinal) }".Replace("<ID>", content_0.Id, StringComparison.Ordinal)
}; };
var result1 = await _.Contents.GraphQlAsync<JToken>(query); var result1 = await _.SharedContents.GraphQlAsync<JToken>(query);
Assert.Equal(1, result1["findMyWritesContent"]["flatData"]["json"]["value"].Value<int>()); Assert.Equal(1, result1["findMyWritesContent"]["flatData"]["json"]["value"].Value<int>());
Assert.Equal(2, result1["findMyWritesContent"]["flatData"]["json"]["obj"]["value"].Value<int>()); Assert.Equal(2, result1["findMyWritesContent"]["flatData"]["json"]["obj"]["value"].Value<int>());
@ -119,8 +119,6 @@ namespace TestSuite.ApiTests
// Do nothing // Do nothing
} }
var countriesClient = _.ClientManager.CreateContentsClient<DynamicEntity, object>("countries");
var query = new var query = new
{ {
query = @" query = @"
@ -143,7 +141,7 @@ namespace TestSuite.ApiTests
}" }"
}; };
var result1 = await countriesClient.GraphQlAsync<JToken>(query); var result1 = await _.SharedContents.GraphQlAsync<JToken>(query);
var typed = result1["queryCountriesContents"].ToObject<List<Country>>(); var typed = result1["queryCountriesContents"].ToObject<List<Country>>();

2
backend/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj

@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="NSwag.Core" Version="13.16.1" /> <PackageReference Include="NSwag.Core" Version="13.16.1" />
<PackageReference Include="PuppeteerSharp" Version="7.1.0" /> <PackageReference Include="PuppeteerSharp" Version="7.1.0" />
<PackageReference Include="Squidex.Assets" Version="4.11.0" /> <PackageReference Include="Squidex.Assets" Version="4.13.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="Verify.Xunit" Version="17.5.0" /> <PackageReference Include="Verify.Xunit" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />

5
backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs

@ -100,6 +100,11 @@ namespace TestSuite.Fixtures
get => ClientManager.CreateUserManagementClient(); get => ClientManager.CreateUserManagementClient();
} }
public IContentsSharedClient<DynamicContent, DynamicData> SharedContents
{
get => ClientManager.CreateSharedDynamicContentsClient();
}
static ClientFixture() static ClientFixture()
{ {
VerifierSettings.IgnoreMember("AppName"); VerifierSettings.IgnoreMember("AppName");

4
backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj

@ -16,8 +16,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.ClientLibrary" Version="10.0.1" /> <PackageReference Include="Squidex.ClientLibrary" Version="11.0.0-beta1" />
<PackageReference Include="Squidex.ClientLibrary.ServiceExtensions" Version="10.0.1" /> <PackageReference Include="Squidex.ClientLibrary.ServiceExtensions" Version="11.0.0-beta1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="Verify" Version="17.5.0" /> <PackageReference Include="Verify" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />

2
frontend/src/app/features/settings/pages/workflows/workflow-step.component.html

@ -1,7 +1,7 @@
<div class="step"> <div class="step">
<div class="row g-0 step-header"> <div class="row g-0 step-header">
<div class="col-auto"> <div class="col-auto">
<button class="btn btn-initial me-1" (click)="makeInitial.emit()" <button class="btn btn-text-secondary btn-initial me-1" (click)="makeInitial.emit()"
[class.enabled]="step.name !== workflow.initial && !step.isLocked" [class.enabled]="step.name !== workflow.initial && !step.isLocked"
[class.active]="step.name === workflow.initial" [class.active]="step.name === workflow.initial"
[disabled]="step.name === workflow.initial || step.isLocked || disabled"> [disabled]="step.name === workflow.initial || step.isLocked || disabled">

Loading…
Cancel
Save