Browse Source

UI for num days.

pull/342/head
Sebastian Stehle 7 years ago
parent
commit
9ce798bffe
  1. 45
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs
  2. 3
      src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs
  3. 2
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/UsageRuleTriggerDto.cs
  4. 16
      src/Squidex/app/features/rules/pages/rules/triggers/usage-trigger.component.html
  5. 13
      src/Squidex/app/features/rules/pages/rules/triggers/usage-trigger.component.ts
  6. 4
      src/Squidex/app/features/schemas/pages/schema/schema-preview-urls-form.component.html
  7. 4
      src/Squidex/app/framework/angular/forms/control-errors.component.ts
  8. 16
      src/Squidex/app/framework/angular/forms/validators.spec.ts
  9. 14
      src/Squidex/app/framework/angular/forms/validators.ts
  10. 40
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs
  11. 72
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/UsageTriggerValidationTests.cs

45
src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs

@ -42,23 +42,52 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
public Task<IEnumerable<ValidationError>> Visit(UsageTrigger trigger)
{
return Task.FromResult(Enumerable.Empty<ValidationError>());
var errors = new List<ValidationError>();
if (trigger.NumDays.HasValue && (trigger.NumDays < 1 || trigger.NumDays > 30))
{
errors.Add(new ValidationError("Num days must be between 1 and 30.", nameof(trigger.NumDays)));
}
return Task.FromResult< IEnumerable<ValidationError>>(errors);
}
public async Task<IEnumerable<ValidationError>> Visit(ContentChangedTriggerV2 trigger)
{
var errors = new List<ValidationError>();
if (trigger.Schemas != null)
{
var schemaErrors = await Task.WhenAll(
trigger.Schemas.Select(async s =>
await SchemaProvider(s.SchemaId) == null
? new ValidationError($"Schema {s.SchemaId} does not exist.", nameof(trigger.Schemas))
: null));
var tasks = new List<Task<ValidationError>>();
foreach (var schema in trigger.Schemas)
{
if (schema.SchemaId == Guid.Empty)
{
errors.Add(new ValidationError("Schema id is required.", nameof(trigger.Schemas)));
}
else
{
tasks.Add(CheckSchemaAsync(schema));
}
}
return schemaErrors.Where(x => x != null).ToList();
var checkErrors = await Task.WhenAll(tasks);
errors.AddRange(checkErrors.Where(x => x != null));
}
return errors;
}
private async Task<ValidationError> CheckSchemaAsync(ContentChangedTriggerSchemaV2 schema)
{
if (await SchemaProvider(schema.SchemaId) == null)
{
return new ValidationError($"Schema {schema.SchemaId} does not exist.", nameof(ContentChangedTriggerV2.Schemas));
}
return new List<ValidationError>();
return null;
}
}
}

3
src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs

@ -24,7 +24,6 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
[Reentrant]
public sealed class UsageTrackerGrain : GrainOfString<UsageTrackerGrain.GrainState>, IRemindable, IUsageTrackerGrain
{
private const int MaxDays = 30;
private readonly IUsageTracker usageTracker;
public sealed class Target
@ -110,7 +109,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
private (DateTime, DateTime) GetDateRange(DateTime today, int? numDays)
{
if (numDays > 0 && numDays < MaxDays)
if (numDays.HasValue)
{
return (today.AddDays(-numDays.Value).AddDays(1), today);
}

2
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/UsageRuleTriggerDto.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Infrastructure.Reflection;
@ -21,6 +22,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
/// <summary>
/// The number of days to check or null for the current month.
/// </summary>
[Range(1, 30)]
public int? NumDays { get; set; }
public override RuleTrigger ToTrigger()

16
src/Squidex/app/features/rules/pages/rules/triggers/usage-trigger.component.html

@ -2,12 +2,24 @@
<div class="form-group">
<label for="condition">Limit</label>
<sqx-control-errors for="condition" [submitted]="triggerFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="limit" [submitted]="triggerFormSubmitted"></sqx-control-errors>
<input type="number" class="form-control" id="limit" formControlName="limit" />
<input type="number" step="1" class="form-control" id="limit" formControlName="limit" />
<small class="form-text text-muted">
The monthly api calls to trigger.
</small>
</div>
<div class="form-group">
<label for="condition">Days</label>
<sqx-control-errors for="numDays" fieldName="Days" [submitted]="triggerFormSubmitted"></sqx-control-errors>
<input type="number" step="1" class="form-control" id="numDays" formControlName="numDays" />
<small class="form-text text-muted">
The number of days to check or empty to check the current month.
</small>
</div>
</div>

13
src/Squidex/app/features/rules/pages/rules/triggers/usage-trigger.component.ts

@ -6,7 +6,9 @@
*/
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ValidatorsEx } from '@app/shared';
@Component({
selector: 'sqx-usage-trigger',
@ -25,6 +27,13 @@ export class UsageTriggerComponent implements OnInit {
public ngOnInit() {
this.triggerForm.setControl('limit',
new FormControl(this.trigger.limit || 20000));
new FormControl(this.trigger.limit || 20000, [
Validators.required
]));
this.triggerForm.setControl('numDays',
new FormControl(this.trigger.numDays, [
ValidatorsEx.between(1, 30)
]));
}
}

4
src/Squidex/app/features/schemas/pages/schema/schema-preview-urls-form.component.html

@ -33,13 +33,13 @@
<div class="form-group row no-gutters" [formGroup]="addForm.form">
<div class="col col-name pr-1">
<sqx-control-errors for="name" fieldName="Name" [submitted]="addForm.submitted | async"></sqx-control-errors>
<sqx-control-errors for="name" [submitted]="addForm.submitted | async"></sqx-control-errors>
<input type="text" class="form-control" maxlength="1000" formControlName="name" placeholder="Name, e.g. Web or Mobile" />
</div>
<div class="col pr-1">
<sqx-control-errors for="url" fieldName="Url" [submitted]="addForm.submitted | async"></sqx-control-errors>
<sqx-control-errors for="url" [submitted]="addForm.submitted | async"></sqx-control-errors>
<input type="text" class="form-control" maxlength="1000" formControlName="url" placeholder="Url" />

4
src/Squidex/app/framework/angular/forms/control-errors.component.ts

@ -15,8 +15,8 @@ const DEFAULT_ERRORS: { [key: string]: string } = {
required: '{field} is required.',
pattern: '{field} does not follow the pattern.',
patternmessage: '{message}',
minvalue: '{field} must be larger than {minValue}.',
maxvalue: '{field} must be smaller than {maxValue}.',
minvalue: '{field} must be larger or equals to {minValue}.',
maxvalue: '{field} must be smaller or equals to {maxValue}.',
minmax: '{field} must have a length of more than {requiredLength}.',
maxlength: '{field} must have a length of less than {requiredLength}.',
match: '{message}',

16
src/Squidex/app/framework/angular/forms/validators.spec.ts

@ -26,6 +26,22 @@ describe('ValidatorsEx.between', () => {
expect(error).toBeNull();
});
it('should return null when value is null', () => {
const input = new FormControl(null);
const error = <any>ValidatorsEx.between(1, 5)(input);
expect(error).toBeNull();
});
it('should return null when value is undefined', () => {
const input = new FormControl(undefined);
const error = <any>ValidatorsEx.between(1, 5)(input);
expect(error).toBeNull();
});
it('should return error when not a number', () => {
const input = new FormControl('text');

14
src/Squidex/app/framework/angular/forms/validators.ts

@ -97,12 +97,14 @@ export module ValidatorsEx {
return (control: AbstractControl) => {
const value: number = control.value;
if (!Types.isNumber(value)) {
return { validnumber: false };
} else if (minValue && value < minValue) {
return { minvalue: { minValue, actualValue: value } };
} else if (maxValue && value > maxValue) {
return { maxvalue: { maxValue, actualValue: value } };
if (!Types.isUndefined(value) && !Types.isNull(value)) {
if (!Types.isNumber(value)) {
return { validnumber: false };
} else if (minValue && value < minValue) {
return { minvalue: { minValue, actualValue: value } };
} else if (maxValue && value > maxValue) {
return { maxvalue: { maxValue, actualValue: value } };
}
}
return null;

40
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs

@ -6,10 +6,13 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using FluentAssertions;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit;
@ -19,23 +22,46 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
{
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly Guid appId = Guid.NewGuid();
private readonly Guid schemaId = Guid.NewGuid();
[Fact]
public async Task Should_add_error_if_schemas_ids_are_not_valid()
public async Task Should_add_error_if_schema_id_is_not_defined()
{
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Create(new ContentChangedTriggerSchemaV2())
};
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Schema id is required.", "Schemas")
});
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored, false))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_add_error_if_schemas_ids_are_not_valid()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId, schemaId, false))
.Returns(Task.FromResult<ISchemaEntity>(null));
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchemaV2()
)
Schemas = ReadOnlyCollection.Create(new ContentChangedTriggerSchemaV2 { SchemaId = schemaId })
};
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
Assert.NotEmpty(errors);
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError($"Schema {schemaId} does not exist.", "Schemas")
});
}
[Fact]
@ -69,9 +95,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchemaV2()
)
Schemas = ReadOnlyCollection.Create(new ContentChangedTriggerSchemaV2 { SchemaId = schemaId })
};
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);

72
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/UsageTriggerValidationTests.cs

@ -0,0 +1,72 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using FluentAssertions;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Infrastructure;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
{
public class UsageTriggerValidationTests
{
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly Guid appId = Guid.NewGuid();
[Fact]
public async Task Should_add_error_if_num_days_less_than_1()
{
var trigger = new UsageTrigger { NumDays = 0 };
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Num days must be between 1 and 30.", "NumDays")
});
}
[Fact]
public async Task Should_add_error_if_num_days_greater_than_30()
{
var trigger = new UsageTrigger { NumDays = 32 };
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
errors.Should().BeEquivalentTo(
new List<ValidationError>
{
new ValidationError("Num days must be between 1 and 30.", "NumDays")
});
}
[Fact]
public async Task Should_not_add_error_if_num_days_is_valid()
{
var trigger = new UsageTrigger { NumDays = 20 };
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
Assert.Empty(errors);
}
[Fact]
public async Task Should_not_add_error_if_num_days_is_not_defined()
{
var trigger = new UsageTrigger();
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
Assert.Empty(errors);
}
}
}
Loading…
Cancel
Save