Browse Source

Merge branch 'release/6.x'

# Conflicts:
#	.github/workflows/marketplace-aws.yml
#	.github/workflows/marketplace-azure.yml
#	.github/workflows/marketplace-digitalocean.yml
#	.github/workflows/marketplace-gcp.yml
#	.github/workflows/marketplace-kubernetes.yml
#	.github/workflows/marketplace-vultr.yml
#	CHANGELOG.md
#	backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentsBulkUpdateCommandMiddleware.cs
pull/909/head
Sebastian 3 years ago
parent
commit
68ed7a8d48
  1. 18
      CHANGELOG.md
  2. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentsBulkUpdateCommandMiddleware.cs
  3. 77
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentsBulkUpdateCommandMiddlewareTests.cs
  4. 87
      backend/tools/TestSuite/TestSuite.ApiTests/ContentUpdateTests.cs
  5. 2
      frontend/src/app/framework/angular/stateful.component.ts
  6. 2
      frontend/src/app/shared/components/contents/content-value.component.html
  7. 44
      frontend/src/app/shared/components/contents/content-value.component.ts

18
CHANGELOG.md

@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [6.13.0] - 200-08-02
### Fkxed
* **UI**: Fixes the rendering of reference lists.
## [6.12.0] - 200-08-01
### Changed
* No changes, just meant to run CI again.
## [6.11.0] - 2022-07-29
### Fixed
* **Assets**: Fix recursive asset deletion. Query was selecting the wrong assets.
## [7.0.0-rc3] - 2022-07-29
### Fixed

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

@ -270,9 +270,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
SimpleMapper.Map(task.Command, command);
SimpleMapper.Map(task.CommandJob, command);
if (!string.IsNullOrWhiteSpace(task.CommandJob.Schema))
var overridenSchema = task.CommandJob.Schema;
if (!string.IsNullOrWhiteSpace(overridenSchema))
{
var schema = await contentQuery.GetSchemaOrThrowAsync(contextProvider.Context, task.Schema, task.CancellationToken);
var schema = await contentQuery.GetSchemaOrThrowAsync(contextProvider.Context, overridenSchema, task.CancellationToken);
command.SchemaId = schema.NamedId();
}

77
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentsBulkUpdateCommandMiddlewareTests.cs

@ -31,6 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
private readonly ICommandBus commandBus = A.Dummy<ICommandBus>();
private readonly NamedId<DomainId> appId = NamedId.Of(DomainId.NewGuid(), "my-app");
private readonly NamedId<DomainId> schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema");
private readonly NamedId<DomainId> schemaCustomId = NamedId.Of(DomainId.NewGuid(), "my-schema2");
private readonly Instant time = Instant.FromDateTimeUtc(DateTime.UtcNow);
private readonly ContentsBulkUpdateCommandMiddleware sut;
@ -96,7 +97,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
schemaId.Name, A<Q>.That.Matches(x => x.JsonQuery == query), ct))
.Returns(ResultList.CreateFrom(2, CreateContent(id), CreateContent(id)));
var command = BulkCommand(BulkUpdateContentType.ChangeStatus, query);
var command = BulkCommand(BulkUpdateContentType.ChangeStatus, new BulkUpdateJob { Query = query });
var result = await PublishAsync(command);
@ -122,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
schemaId.Name, A<Q>.That.Matches(x => x.JsonQuery == query), ct))
.Returns(ResultList.CreateFrom(1, CreateContent(id)));
var command = BulkCommand(BulkUpdateContentType.Upsert, query: query, data: data);
var command = BulkCommand(BulkUpdateContentType.Upsert, new BulkUpdateJob { Query = query, Data = data });
var result = await PublishAsync(command);
@ -154,7 +155,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
CreateContent(id1),
CreateContent(id2)));
var command = BulkCommand(BulkUpdateContentType.Upsert, query: query, data: data);
var command = BulkCommand(BulkUpdateContentType.Upsert, new BulkUpdateJob { Query = query, Data = data });
command.Jobs![0].ExpectedCount = 2;
@ -180,7 +181,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (_, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Upsert, data: data);
var command = BulkCommand(BulkUpdateContentType.Upsert, new BulkUpdateJob { Data = data });
var result = await PublishAsync(command);
@ -199,7 +200,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (_, data, query) = CreateTestData(true);
var command = BulkCommand(BulkUpdateContentType.Upsert, query: query, data: data);
var command = BulkCommand(BulkUpdateContentType.Upsert, new BulkUpdateJob { Query = query, Data = data });
var result = await PublishAsync(command);
@ -218,7 +219,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Upsert, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Upsert, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -237,7 +238,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(true);
var command = BulkCommand(BulkUpdateContentType.Upsert, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Upsert, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -256,7 +257,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Create, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Create, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -275,7 +276,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Create, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Create, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -293,7 +294,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Update, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Update, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -312,7 +313,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Update, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Update, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -330,7 +331,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Patch, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Patch, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -349,7 +350,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, data, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Delete, id: id, data: data);
var command = BulkCommand(BulkUpdateContentType.Delete, new BulkUpdateJob { Data = data }, id);
var result = await PublishAsync(command);
@ -386,7 +387,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
var (id, _, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.ChangeStatus, id: id, dueTime: time);
var command = BulkCommand(BulkUpdateContentType.ChangeStatus, new BulkUpdateJob { DueTime = time }, id);
var result = await PublishAsync(command);
@ -490,6 +491,28 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
.MustNotHaveHappened();
}
[Fact]
public async Task Should_override_schema_name()
{
SetupContext(Permissions.AppContentsDeleteOwn);
A.CallTo(() => contentQuery.GetSchemaOrThrowAsync(A<Context>._, schemaCustomId.Name, ct))
.Returns(Mocks.Schema(appId, schemaCustomId));
var (id, _, _) = CreateTestData(false);
var command = BulkCommand(BulkUpdateContentType.Delete, new BulkUpdateJob { Schema = schemaCustomId.Name }, id);
var result = await PublishAsync(command);
Assert.Single(result);
Assert.Single(result, x => x.JobIndex == 0 && x.Id == id && x.Exception == null);
A.CallTo(() => commandBus.PublishAsync(
A<DeleteContent>.That.Matches(x => x.SchemaId == schemaCustomId), ct))
.MustHaveHappened();
}
private async Task<BulkUpdateResult> PublishAsync(ICommand command)
{
var context = new CommandContext(command, commandBus);
@ -499,22 +522,18 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
return (context.PlainResult as BulkUpdateResult)!;
}
private BulkUpdateContents BulkCommand(BulkUpdateContentType type, Query<JsonValue>? query = null,
DomainId? id = null, ContentData? data = null, Instant? dueTime = null)
private BulkUpdateContents BulkCommand(BulkUpdateContentType type, BulkUpdateJob? job = null, DomainId? id = null)
{
job ??= new BulkUpdateJob();
job.Id = id;
job.Type = type;
return new BulkUpdateContents
{
AppId = appId,
Jobs = new[]
{
new BulkUpdateJob
{
Type = type,
Id = id,
Data = data!,
DueTime = dueTime,
Query = query
}
job
},
SchemaId = schemaId
};
@ -522,12 +541,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
private Context SetupContext(string id)
{
var permission = Permissions.ForApp(id, appId.Name, schemaId.Name).Id;
var claimsIdentity = new ClaimsIdentity();
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
claimsIdentity.AddClaim(new Claim(SquidexClaimTypes.Permissions, permission));
claimsIdentity.AddClaim(
new Claim(SquidexClaimTypes.Permissions,
Permissions.ForApp(id, appId.Name, schemaId.Name).Id));
claimsIdentity.AddClaim(
new Claim(SquidexClaimTypes.Permissions,
Permissions.ForApp(id, appId.Name, schemaCustomId.Name).Id));
var requestContext = new Context(claimsPrincipal, Mocks.App(appId));

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

@ -506,7 +506,7 @@ namespace TestSuite.ApiTests
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish);
// STEP 2: Path an item.
// STEP 2: Patch an item.
await _.Contents.PatchAsync(content.Id, new TestEntityData { Number = 1 });
@ -591,7 +591,7 @@ namespace TestSuite.ApiTests
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish);
// STEP 2: Path an item.
// STEP 2: Patch an item.
await _.Contents.UpsertAsync(content.Id, new TestEntityData { Number = 1 }, ContentUpsertOptions.AsPatch);
@ -624,7 +624,7 @@ namespace TestSuite.ApiTests
content = await _.Contents.CreateAsync(new TestEntityData { String = "test" }, ContentCreateOptions.AsPublish);
// STEP 2: Path an item.
// STEP 2: Patch an item.
await _.Contents.BulkUpdateAsync(new BulkUpdate
{
Jobs = new List<BulkUpdateJob>
@ -681,6 +681,87 @@ namespace TestSuite.ApiTests
}
}
[Fact]
public async Task Should_update_content_with_bulk_and_overriden_schema_name()
{
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.
var client = _.ClientManager.CreateContentsClient<TestEntity, TestEntityData>(schemaName);
await client.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 client.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]
public async Task Should_create_draft_version()
{

2
frontend/src/app/framework/angular/stateful.component.ts

@ -83,7 +83,7 @@ export abstract class StatefulComponent<T = any> extends State<T> implements OnD
this.changeDetector.detectChanges();
}
public own<R>(subscription: Subscription | UnsubscribeFunction | Observable<R>) {
public own<R>(subscription: Subscription | UnsubscribeFunction | Observable<R> | null | undefined) {
this.subscriptions.own(subscription);
}
}

2
frontend/src/app/shared/components/contents/content-value.component.html

@ -1,6 +1,6 @@
<ng-container *ngIf="isPlain; else html">
<div class="value-container" [title]="title" titlePosition="top-left">
<div #valueElement [class.truncate]="!wrapping">{{value}}</div>
<div #valueElement [class.truncate]="!snapshot.wrapping">{{value}}</div>
</div>
<button class="btn btn-icon" *ngIf="isString && fields" (click)="toggle()" sqxStopClick>

44
frontend/src/app/shared/components/contents/content-value.component.ts

@ -6,8 +6,12 @@
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { ResourceOwner } from '@app/framework';
import { FieldWrappings, HtmlValue, TableField, TableSettings, Types } from '@app/shared/internal';
import { StatefulComponent } from '@app/framework';
import { HtmlValue, TableField, TableSettings, Types } from '@app/shared/internal';
interface State {
wrapping: boolean;
}
@Component({
selector: 'sqx-content-value[value]',
@ -15,34 +19,30 @@ import { FieldWrappings, HtmlValue, TableField, TableSettings, Types } from '@ap
templateUrl: './content-value.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ContentValueComponent extends ResourceOwner implements OnChanges {
export class ContentValueComponent extends StatefulComponent<State> implements OnChanges {
@Input()
public value!: any;
@Input()
public field!: TableField;
public field?: TableField;
@Input()
public fields?: TableSettings;
public wrapping = false;
public get title() {
return this.isString && this.isPlain ? this.value : undefined;
}
public get isString() {
return this.field.rootField?.properties.fieldType === 'String';
return this.field?.rootField?.properties.fieldType === 'String';
}
public get isPlain() {
return !Types.is(this.value, HtmlValue);
}
public get title() {
return this.isString ? this.value : undefined;
}
constructor(
private readonly changeDetector: ChangeDetectorRef,
) {
super();
constructor(changeDetector: ChangeDetectorRef) {
super(changeDetector, { wrapping: false });
}
public ngOnChanges(changes: SimpleChanges) {
@ -51,24 +51,18 @@ export class ContentValueComponent extends ResourceOwner implements OnChanges {
this.own(this.fields?.fieldWrappings
.subscribe(wrappings => {
this.updateWrapping(wrappings);
const wrapping = wrappings[this.field?.name!];
this.next({ wrapping });
}));
}
}
public toggle() {
this.fields?.toggleWrapping(this.field?.name);
}
private updateWrapping(wrappings: FieldWrappings) {
const wrapping = wrappings[this.field?.name];
if (wrapping === this.wrapping) {
if (!this.field) {
return;
}
this.wrapping = wrapping;
this.changeDetector.detectChanges();
this.fields?.toggleWrapping(this.field?.name);
}
}

Loading…
Cancel
Save