Browse Source

Fix for validate on publish. (#680)

pull/681/head
Sebastian Stehle 5 years ago
committed by GitHub
parent
commit
e59cfe2d93
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentExtensions.cs
  2. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.State.cs
  3. 18
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs
  4. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs
  5. 6
      frontend/app/shared/state/assets.state.ts
  6. 8
      frontend/app/shared/state/contents.state.ts

5
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentExtensions.cs

@ -45,6 +45,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
return content.NewStatus ?? content.Status; return content.NewStatus ?? content.Status;
} }
public static bool IsPublished(this IContentEntity content)
{
return content.EditingStatus() == Status.Published;
}
public static SearchScope Scope(this Context context) public static SearchScope Scope(this Context context)
{ {
return context.ShouldProvideUnpublished() || context.IsFrontendClient ? SearchScope.All : SearchScope.Published; return context.ShouldProvideUnpublished() || context.IsFrontendClient ? SearchScope.All : SearchScope.Published;

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

@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
[IgnoreDataMember] [IgnoreDataMember]
public Status Status public Status Status
{ {
get => CurrentVersion.Status; get => CurrentVersion?.Status ?? Status.Draft;
} }
public override bool ApplyEvent(IEvent @event, EnvelopeHeaders headers) public override bool ApplyEvent(IEvent @event, EnvelopeHeaders headers)

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

@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
operation.MustHavePermission(Permissions.AppContentsReadOwn); operation.MustHavePermission(Permissions.AppContentsReadOwn);
await operation.ValidateContentAndInputAsync(Snapshot.Data, false); await operation.ValidateContentAndInputAsync(Snapshot.Data, false, Snapshot.IsPublished());
return true; return true;
}); });
@ -226,7 +226,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {
await operation.ValidateInputAsync(c.Data, c.OptimizeValidation); await operation.ValidateInputAsync(c.Data, c.OptimizeValidation, Snapshot.IsPublished());
} }
var status = await operation.GetInitialStatusAsync(); var status = await operation.GetInitialStatusAsync();
@ -240,7 +240,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {
await operation.ValidateContentAsync(c.Data, c.OptimizeValidation); await operation.ValidateContentAsync(c.Data, c.OptimizeValidation, Snapshot.IsPublished());
} }
Create(c, status); Create(c, status);
@ -293,7 +293,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate && c.Status == Status.Published && operation.SchemaDef.Properties.ValidateOnPublish) if (!c.DoNotValidate && c.Status == Status.Published && operation.SchemaDef.Properties.ValidateOnPublish)
{ {
await operation.ValidateContentAndInputAsync(Snapshot.Data, c.OptimizeValidation); await operation.ValidateContentAndInputAsync(Snapshot.Data, c.OptimizeValidation, true);
} }
ChangeStatus(c); ChangeStatus(c);
@ -306,7 +306,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {
await operation.ValidateInputPartialAsync(c.Data, c.OptimizeValidation); await operation.ValidateInputPartialAsync(c.Data, c.OptimizeValidation, Snapshot.IsPublished());
} }
if (!c.DoNotValidateWorkflow) if (!c.DoNotValidateWorkflow)
@ -328,7 +328,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {
await operation.ValidateContentAsync(newData, c.OptimizeValidation); await operation.ValidateContentAsync(newData, c.OptimizeValidation, Snapshot.IsPublished());
} }
Update(c, newData); Update(c, newData);
@ -341,7 +341,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {
await operation.ValidateInputPartialAsync(c.Data, c.OptimizeValidation); await operation.ValidateInputPartialAsync(c.Data, c.OptimizeValidation, Snapshot.IsPublished());
} }
if (!c.DoNotValidateWorkflow) if (!c.DoNotValidateWorkflow)
@ -363,7 +363,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
if (!c.DoNotValidate) if (!c.DoNotValidate)
{ {
await operation.ValidateContentAsync(newData, c.OptimizeValidation); await operation.ValidateContentAsync(newData, c.OptimizeValidation, Snapshot.IsPublished());
} }
Update(c, newData); Update(c, newData);
@ -438,7 +438,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject
{ {
return StatusChange.Published; return StatusChange.Published;
} }
else if (Snapshot.EditingStatus() == Status.Published) else if (Snapshot.IsPublished())
{ {
return StatusChange.Unpublished; return StatusChange.Unpublished;
} }

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

@ -48,36 +48,36 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
} }
} }
public static async Task ValidateInputAsync(this OperationContext context, ContentData data, bool optimize) public static async Task ValidateInputAsync(this OperationContext context, ContentData data, bool optimize, bool published)
{ {
var validator = GetValidator(context, optimize); var validator = GetValidator(context, optimize, published);
await validator.ValidateInputAsync(data); await validator.ValidateInputAsync(data);
context.AddErrors(validator.Errors).ThrowOnErrors(); context.AddErrors(validator.Errors).ThrowOnErrors();
} }
public static async Task ValidateInputPartialAsync(this OperationContext context, ContentData data, bool optimize) public static async Task ValidateInputPartialAsync(this OperationContext context, ContentData data, bool optimize, bool published)
{ {
var validator = GetValidator(context, optimize); var validator = GetValidator(context, optimize, published);
await validator.ValidateInputPartialAsync(data); await validator.ValidateInputPartialAsync(data);
context.AddErrors(validator.Errors).ThrowOnErrors(); context.AddErrors(validator.Errors).ThrowOnErrors();
} }
public static async Task ValidateContentAsync(this OperationContext context, ContentData data, bool optimize) public static async Task ValidateContentAsync(this OperationContext context, ContentData data, bool optimize, bool published)
{ {
var validator = GetValidator(context, optimize); var validator = GetValidator(context, optimize, published);
await validator.ValidateContentAsync(data); await validator.ValidateContentAsync(data);
context.AddErrors(validator.Errors).ThrowOnErrors(); context.AddErrors(validator.Errors).ThrowOnErrors();
} }
public static async Task ValidateContentAndInputAsync(this OperationContext operation, ContentData data, bool optimize) public static async Task ValidateContentAndInputAsync(this OperationContext operation, ContentData data, bool optimize, bool published)
{ {
var validator = GetValidator(operation, optimize); var validator = GetValidator(operation, optimize, published);
await validator.ValidateInputAsync(data); await validator.ValidateInputAsync(data);
await validator.ValidateContentAsync(data); await validator.ValidateContentAsync(data);
@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
} }
} }
private static ContentValidator GetValidator(this OperationContext context, bool optimize) private static ContentValidator GetValidator(this OperationContext context, bool optimize, bool published)
{ {
var validationContext = var validationContext =
new ValidationContext(context.Resolve<IJsonSerializer>(), new ValidationContext(context.Resolve<IJsonSerializer>(),
@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.DomainObject.Guards
context.Schema.NamedId(), context.Schema.NamedId(),
context.SchemaDef, context.SchemaDef,
context.ContentId) context.ContentId)
.Optimized(optimize); .Optimized(optimize).AsPublishing(published);
var validator = var validator =
new ContentValidator(context.Partition(), new ContentValidator(context.Partition(),

6
frontend/app/shared/state/assets.state.ts

@ -286,7 +286,7 @@ export abstract class AssetsStateBase extends State<Snapshot> {
public deleteAsset(asset: AssetDto) { public deleteAsset(asset: AssetDto) {
return this.assetsService.deleteAssetItem(this.appName, asset, true, asset.version).pipe( return this.assetsService.deleteAssetItem(this.appName, asset, true, asset.version).pipe(
catchError((error: ErrorDto) => { catchError((error: ErrorDto) => {
if (error.statusCode === 400) { if (isReferrerError(error)) {
return this.dialogs.confirm( return this.dialogs.confirm(
'i18n:assets.deleteReferrerConfirmTitle', 'i18n:assets.deleteReferrerConfirmTitle',
'i18n:assets.deleteReferrerConfirmText', 'i18n:assets.deleteReferrerConfirmText',
@ -401,6 +401,10 @@ export abstract class AssetsStateBase extends State<Snapshot> {
} }
} }
function isReferrerError(error?: ErrorDto) {
return error?.statusCode === 400 && (!error?.details || error?.details.length === 0);
}
function updateTags(snapshot: Snapshot, newAsset?: AssetDto, oldAsset?: AssetDto) { function updateTags(snapshot: Snapshot, newAsset?: AssetDto, oldAsset?: AssetDto) {
if (!oldAsset && newAsset) { if (!oldAsset && newAsset) {
oldAsset = snapshot.assets.find(x => x.id === newAsset.id); oldAsset = snapshot.assets.find(x => x.id === newAsset.id);

8
frontend/app/shared/state/contents.state.ts

@ -6,7 +6,7 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DialogService, getPagingInfo, ListState, shareSubscribed, State, Types, Version, Versioned } from '@app/framework'; import { DialogService, ErrorDto, getPagingInfo, ListState, shareSubscribed, State, Types, Version, Versioned } from '@app/framework';
import { EMPTY, Observable, of } from 'rxjs'; import { EMPTY, Observable, of } from 'rxjs';
import { catchError, finalize, map, switchMap, tap } from 'rxjs/operators'; import { catchError, finalize, map, switchMap, tap } from 'rxjs/operators';
import { BulkResultDto, BulkUpdateJobDto, ContentDto, ContentsDto, ContentsService, StatusInfo } from './../services/contents.service'; import { BulkResultDto, BulkUpdateJobDto, ContentDto, ContentsDto, ContentsService, StatusInfo } from './../services/contents.service';
@ -342,7 +342,7 @@ export abstract class ContentsStateBase extends State<Snapshot> {
private bulkWithRetry(contents: ReadonlyArray<ContentDto>, job: Partial<BulkUpdateJobDto>, confirmTitle: string, confirmText: string, confirmKey: string): Observable<ReadonlyArray<BulkResultDto>> { private bulkWithRetry(contents: ReadonlyArray<ContentDto>, job: Partial<BulkUpdateJobDto>, confirmTitle: string, confirmText: string, confirmKey: string): Observable<ReadonlyArray<BulkResultDto>> {
return this.bulkMany(contents, true, job).pipe( return this.bulkMany(contents, true, job).pipe(
switchMap(results => { switchMap(results => {
const failed = contents.filter(x => results.find(r => r.contentId === x.id)?.error?.statusCode === 400); const failed = contents.filter(x => isReferrerError(results.find(r => r.contentId === x.id)?.error));
if (failed.length > 0) { if (failed.length > 0) {
return this.dialogs.confirm(confirmTitle, confirmText, confirmKey).pipe( return this.dialogs.confirm(confirmTitle, confirmText, confirmKey).pipe(
@ -395,6 +395,10 @@ export abstract class ContentsStateBase extends State<Snapshot> {
public abstract get schemaName(): string; public abstract get schemaName(): string;
} }
function isReferrerError(error?: ErrorDto) {
return error?.statusCode === 400 && (!error?.details || error?.details.length === 0);
}
@Injectable() @Injectable()
export class ContentsState extends ContentsStateBase { export class ContentsState extends ContentsStateBase {
constructor(appsState: AppsState, contentsService: ContentsService, dialogs: DialogService, constructor(appsState: AppsState, contentsService: ContentsService, dialogs: DialogService,

Loading…
Cancel
Save