Browse Source

Rule UI updated.

pull/157/head
Sebastian Stehle 9 years ago
parent
commit
6574f2ac5f
  1. 1
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs
  2. 14
      src/Squidex.Domain.Apps.Core.Model/SquidexCoreModel.cs
  3. 14
      src/Squidex.Domain.Apps.Events/SquidexEvents.cs
  4. 2
      src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository.cs
  5. 14
      src/Squidex.Infrastructure/SquidexInfrastructure.cs
  6. 36
      src/Squidex.Infrastructure/TypeNameRegistry.cs
  7. 10
      src/Squidex/Config/Domain/Serializers.cs
  8. 72
      src/Squidex/app/features/rules/pages/rules/rules-page.component.html
  9. 24
      src/Squidex/app/features/rules/pages/rules/rules-page.component.scss
  10. 44
      src/Squidex/app/features/rules/pages/rules/rules-page.component.ts
  11. 4
      src/Squidex/app/framework/angular/toggle.component.scss
  12. 2
      src/Squidex/app/framework/angular/toggle.component.ts
  13. 6
      src/Squidex/app/shared/services/rules.service.ts
  14. 2
      tests/Benchmarks/Tests/HandleEvents.cs
  15. 2
      tests/Benchmarks/Tests/HandleEventsWithManyWriters.cs
  16. 2
      tests/Squidex.Infrastructure.Tests/Actors/ActorRemoteTests.cs
  17. 4
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs
  18. 22
      tests/Squidex.Infrastructure.Tests/TypeNameRegistryTests.cs

1
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs

@ -84,7 +84,6 @@ namespace Squidex.Domain.Apps.Core.Schemas
new TagsField(id, name, partitioning, (TagsFieldProperties)properties));
typeNameRegistry.MapObsolete(typeof(ReferencesFieldProperties), "DateTime");
typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "References");
}

14
src/Squidex.Domain.Apps.Core.Model/SquidexCoreModel.cs

@ -0,0 +1,14 @@
// ==========================================================================
// SquidexCoreModel.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Core
{
public static class SquidexCoreModel
{
}
}

14
src/Squidex.Domain.Apps.Events/SquidexEvents.cs

@ -0,0 +1,14 @@
// ==========================================================================
// SquidexEvents.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Events
{
public static class SquidexEvents
{
}
}

2
src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository.cs

@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
{
await EnsureRulesLoadedAsync();
return inMemoryRules.GetOrDefault(appId) ?? EmptyRules;
return inMemoryRules.GetOrDefault(appId)?.ToList() ?? EmptyRules;
}
private async Task EnsureRulesLoadedAsync()

14
src/Squidex.Infrastructure/SquidexInfrastructure.cs

@ -0,0 +1,14 @@
// ==========================================================================
// SquidexInfrastructure.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Infrastructure
{
public static class SquidexInfrastructure
{
}
}

36
src/Squidex.Infrastructure/TypeNameRegistry.cs

@ -17,20 +17,6 @@ namespace Squidex.Infrastructure
private readonly Dictionary<Type, string> namesByType = new Dictionary<Type, string>();
private readonly Dictionary<string, Type> typesByName = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public TypeNameRegistry Map(Type type)
{
Guard.NotNull(type, nameof(type));
var typeNameAttribute = type.GetTypeInfo().GetCustomAttribute<TypeNameAttribute>();
if (typeNameAttribute != null)
{
Map(type, typeNameAttribute.TypeName);
}
return this;
}
public TypeNameRegistry MapObsolete(Type type, string name)
{
Guard.NotNull(type, nameof(type));
@ -56,6 +42,20 @@ namespace Squidex.Infrastructure
return this;
}
public TypeNameRegistry Map(Type type)
{
Guard.NotNull(type, nameof(type));
var typeNameAttribute = type.GetTypeInfo().GetCustomAttribute<TypeNameAttribute>();
if (!string.IsNullOrWhiteSpace(typeNameAttribute?.TypeName))
{
Map(type, typeNameAttribute.TypeName);
}
return this;
}
public TypeNameRegistry Map(Type type, string name)
{
Guard.NotNull(type, nameof(type));
@ -95,15 +95,13 @@ namespace Squidex.Infrastructure
return this;
}
public TypeNameRegistry Map(Assembly assembly)
public TypeNameRegistry MapUnmapped(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var typeNameAttribute = type.GetTypeInfo().GetCustomAttribute<TypeNameAttribute>();
if (!string.IsNullOrWhiteSpace(typeNameAttribute?.TypeName))
if (!namesByType.ContainsKey(type))
{
Map(type, typeNameAttribute.TypeName);
Map(type);
}
}

10
src/Squidex/Config/Domain/Serializers.cs

@ -6,18 +6,18 @@
// All rights reserved.
// ==========================================================================
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NodaTime;
using NodaTime.Serialization.JsonNet;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps.Json;
using Squidex.Domain.Apps.Core.Rules.Json;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
@ -45,6 +45,7 @@ namespace Squidex.Config.Domain
new NamedStringIdConverter(),
new PropertiesBagConverter(),
new RefTokenConverter(),
new RuleConverter(),
new SchemaConverter(FieldRegistry),
new StringEnumConverter());
@ -62,8 +63,9 @@ namespace Squidex.Config.Domain
static Serializers()
{
TypeNameRegistry.Map(typeof(SquidexEvent).GetTypeInfo().Assembly);
TypeNameRegistry.Map(typeof(NoopEvent).GetTypeInfo().Assembly);
TypeNameRegistry.MapUnmapped(typeof(SquidexCoreModel).Assembly);
TypeNameRegistry.MapUnmapped(typeof(SquidexEvents).Assembly);
TypeNameRegistry.MapUnmapped(typeof(SquidexInfrastructure).Assembly);
ConfigureJson(SerializerSettings, TypeNameHandling.Auto);

72
src/Squidex/app/features/rules/pages/rules/rules-page.component.html

@ -1,6 +1,6 @@
<sqx-title message="{app} | Rules" parameter1="app" value1="{{appName() | async}}"></sqx-title>
<sqx-panel panelWidth="50rem">
<sqx-panel panelWidth="54rem">
<div class="panel-header">
<div class="panel-title-row">
<div class="float-right">
@ -29,14 +29,69 @@
<div class="table-items-row table-items-row-empty" *ngIf="rules && rules.length === 0">
No Rule created yet.
</div>
<table class="table table-items table-fixed" *ngIf="rules && rules.length > 0">
<colgroup>
<col style="width: 40px" />
<col style="width: 50%" />
<col style="width: 50px" />
<col style="width: 50%" />
<col style="width: 50px" />
<col style="width: 70px" />
</colgroup>
<tbody>
<ng-template ngFor let-rule [ngForOf]="rules">
<tr>
<td class="step-if">
<h3>If</h3>
</td>
<td>
<span class="rule-element rule-element-{{rule.triggerType}}">
<span class="rule-element-icon">
<i class="icon-trigger-{{rule.triggerType}}"></i>
</span>
<span class="rule-element-text">
{{ruleTriggers[rule.triggerType]}}
</span>
</span>
</td>
<td class="step-then">
<h3>then</h3>
</td>
<td>
<span class="rule-element rule-element-{{rule.actionType}}">
<span class="rule-element-icon">
<i class="icon-action-{{rule.actionType}}"></i>
</span>
<span class="rule-element-text">
{{ruleActions[rule.actionType]}}
</span>
</span>
</td>
<td>
<sqx-toggle [ngModel]="rule.isEnabled" (ngModelChange)="toggleRule(rule)"></sqx-toggle>
</td>
<td>
<button type="button" class="btn btn-link btn-danger"
(sqxConfirmClick)="deleteRule(rule)"
confirmTitle="Delete rule"
confirmText="Do you really want to delete the rule?">
<i class="icon-bin2"></i>
</button>
</td>
</tr>
<tr class="spacer"></tr>
</ng-template>
</tbody>
</table>
</div>
</div>
<div class="panel-sidebar">
<a class="panel-link" routerLink="events" routerLinkActive="active" #linkHistory>
<i class="icon-time"></i>
</a>
<div class="panel-sidebar">
<a class="panel-link" routerLink="events" routerLinkActive="active" #linkHistory>
<i class="icon-time"></i>
</a>
</div>
</div>
</sqx-panel>
@ -44,7 +99,8 @@
<div class="modal-backdrop"></div>
<sqx-rule-wizard [schemas]="schemas"
(cancelled)="addRuleDialog.hide()">
(cancelled)="addRuleDialog.hide()"
(created)="onRuleCreated($event)">
</sqx-rule-wizard>
</div>

24
src/Squidex/app/features/rules/pages/rules/rules-page.component.scss

@ -1,10 +1,26 @@
@import '_vars';
@import '_mixins';
.failed {
color: $color-theme-error;
sqx-toggle {
display: inline-block;
}
.success {
color: $color-theme-green;
.table-items {
tbody {
td {
padding-bottom: .5rem;
}
}
}
.step-if {
padding-left: 1.25rem;
padding-right: 0;
text-align: left;
}
.step-then {
padding-left: 0;
padding-right: 0;
text-align: center;
}

44
src/Squidex/app/features/rules/pages/rules/rules-page.component.ts

@ -11,10 +11,13 @@ import {
AppComponentBase,
AppsStoreService,
AuthService,
DateTime,
DialogService,
fadeAnimation,
ImmutableArray,
ModalView,
ruleActions,
ruleTriggers,
RuleDto,
RulesService,
SchemaDto,
@ -30,7 +33,10 @@ import {
]
})
export class RulesPageComponent extends AppComponentBase implements OnInit {
public addRuleDialog = new ModalView(true, false);
public ruleActions = ruleActions;
public ruleTriggers = ruleTriggers;
public addRuleDialog = new ModalView();
public rules: ImmutableArray<RuleDto>;
public schemas: SchemaDto[];
@ -63,4 +69,40 @@ export class RulesPageComponent extends AppComponentBase implements OnInit {
this.notifyError(error);
});
}
public onRuleCreated(rule: RuleDto) {
this.rules = this.rules.push(rule);
this.addRuleDialog.hide();
}
public toggleRule(rule: RuleDto) {
if (rule.isEnabled) {
this.appNameOnce()
.switchMap(app => this.rulesService.disableRule(app, rule.id, rule.version))
.subscribe(dto => {
this.rules = this.rules.replace(rule, rule.disable(this.authService.user.id, dto.version, DateTime.now()));
}, error => {
this.notifyError(error);
});
} else {
this.appNameOnce()
.switchMap(app => this.rulesService.enableRule(app, rule.id, rule.version))
.subscribe(dto => {
this.rules = this.rules.replace(rule, rule.enable(this.authService.user.id, dto.version, DateTime.now()));
}, error => {
this.notifyError(error);
});
}
}
public deleteRule(rule: RuleDto) {
this.appNameOnce()
.switchMap(app => this.rulesService.deleteRule(app, rule.id, rule.version))
.subscribe(dto => {
this.rules = this.rules.remove(rule);
}, error => {
this.notifyError(error);
});
}
}

4
src/Squidex/app/framework/angular/toggle.component.scss

@ -1,8 +1,8 @@
@import '_mixins';
@import '_vars';
$toggle-width: 3.2rem;
$toggle-height: 2rem;
$toggle-width: 2.2rem;
$toggle-height: 1.4rem;
$toggle-button-size: $toggle-height - .3rem;
.toggle {

2
src/Squidex/app/framework/angular/toggle.component.ts

@ -28,7 +28,7 @@ export class ToggleComponent implements ControlValueAccessor {
public isDisabled = false;
public writeValue(value: boolean | null | undefined) {
this.isChecked = Types.isBoolean(value) ? value || null : null;
this.isChecked = Types.isBoolean(value) ? value : null;
}
public setDisabledState(isDisabled: boolean): void {

6
src/Squidex/app/shared/services/rules.service.ts

@ -25,7 +25,7 @@ export const ruleTriggers: any = {
};
export const ruleActions: any = {
'Webhook': 'Send Webhooks'
'Webhook': 'Send Webhook'
};
export class RuleDto {
@ -52,9 +52,9 @@ export class RuleDto {
version,
this.isEnabled,
update.trigger,
update.trigger['triggerType'],
update.trigger.triggerType,
update.action,
update.action['actionType']);
update.action.actionType);
}
public enable(user: string, version: Version, now?: DateTime): RuleDto {

2
tests/Benchmarks/Tests/HandleEvents.cs

@ -21,7 +21,7 @@ namespace Benchmarks.Tests
public sealed class HandleEvents : IBenchmark
{
private const int NumEvents = 5000;
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry().Map(typeof(MyEvent));
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry().MapUnmapped(typeof(MyEvent));
private readonly EventDataFormatter formatter;
private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
private IMongoClient mongoClient;

2
tests/Benchmarks/Tests/HandleEventsWithManyWriters.cs

@ -23,7 +23,7 @@ namespace Benchmarks.Tests
{
private const int NumCommits = 200;
private const int NumStreams = 10;
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry().Map(typeof(MyEvent));
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry().MapUnmapped(typeof(MyEvent));
private readonly EventDataFormatter formatter;
private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
private IMongoClient mongoClient;

2
tests/Squidex.Infrastructure.Tests/Actors/ActorRemoteTests.cs

@ -49,7 +49,7 @@ namespace Squidex.Infrastructure.Actors
public ActorRemoteTests()
{
registry.Map(typeof(SuccessMessage));
registry.MapUnmapped(typeof(SuccessMessage));
actors = new RemoteActors(new DefaultRemoteActorChannel(new InMemoryPubSub(), registry));
actors.Connect("my", actor);

4
tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs

@ -40,8 +40,8 @@ namespace Squidex.Infrastructure.CQRS.Events
{
serializerSettings.Converters.Add(new PropertiesBagConverter());
typeNameRegistry.Map(typeof(MyEvent), "Event");
typeNameRegistry.Map(typeof(MyOldEvent), "OldEvent");
typeNameRegistry.MapUnmapped(typeof(MyEvent), "Event");
typeNameRegistry.MapUnmapped(typeof(MyOldEvent), "OldEvent");
sut = new EventDataFormatter(typeNameRegistry, serializerSettings);
}

22
tests/Squidex.Infrastructure.Tests/TypeNameRegistryTests.cs

@ -30,7 +30,7 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_register_and_retrieve_types()
{
sut.Map(typeof(int), "NumberField");
sut.MapUnmapped(typeof(int), "NumberField");
Assert.Equal("NumberField", sut.GetName<int>());
Assert.Equal("NumberField", sut.GetName(typeof(int)));
@ -42,7 +42,7 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_register_from_attribute()
{
sut.Map(typeof(MyType));
sut.MapUnmapped(typeof(MyType));
Assert.Equal("my", sut.GetName<MyType>());
Assert.Equal("my", sut.GetName(typeof(MyType)));
@ -54,7 +54,7 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_register_from_assembly()
{
sut.Map(typeof(TypeNameRegistryTests).GetTypeInfo().Assembly);
sut.MapUnmapped(typeof(TypeNameRegistryTests).GetTypeInfo().Assembly);
Assert.Equal("my", sut.GetName<MyType>());
Assert.Equal("my", sut.GetName(typeof(MyType)));
@ -66,7 +66,7 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_register_event_type_from_assembly()
{
sut.Map(typeof(TypeNameRegistryTests).GetTypeInfo().Assembly);
sut.MapUnmapped(typeof(TypeNameRegistryTests).GetTypeInfo().Assembly);
Assert.Equal("MyAddedEventV2", sut.GetName<MyAdded>());
Assert.Equal("MyAddedEventV2", sut.GetName(typeof(MyAdded)));
@ -78,7 +78,7 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_register_fallback_name()
{
sut.Map(typeof(MyType));
sut.MapUnmapped(typeof(MyType));
sut.MapObsolete(typeof(MyType), "my-old");
Assert.Equal(typeof(MyType), sut.GetType("my"));
@ -88,24 +88,24 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_not_throw_exception_if_type_is_already_registered_with_same_name()
{
sut.Map(typeof(long), "long");
sut.Map(typeof(long), "long");
sut.MapUnmapped(typeof(long), "long");
sut.MapUnmapped(typeof(long), "long");
}
[Fact]
public void Should_throw_exception_if_type_is_already_registered()
{
sut.Map(typeof(long), "long");
sut.MapUnmapped(typeof(long), "long");
Assert.Throws<ArgumentException>(() => sut.Map(typeof(long), "longer"));
Assert.Throws<ArgumentException>(() => sut.MapUnmapped(typeof(long), "longer"));
}
[Fact]
public void Should_throw_exception_if_name_is_already_registered()
{
sut.Map(typeof(short), "short");
sut.MapUnmapped(typeof(short), "short");
Assert.Throws<ArgumentException>(() => sut.Map(typeof(byte), "short"));
Assert.Throws<ArgumentException>(() => sut.MapUnmapped(typeof(byte), "short"));
}
[Fact]

Loading…
Cancel
Save