Browse Source

Fixes tracking and inputs.

pull/1021/head
Sebastian 3 years ago
parent
commit
fb789f7415
  1. 4
      backend/src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs
  2. 15
      backend/src/Squidex.Infrastructure/UsageTracking/Counters.cs
  3. 34
      backend/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs
  4. 4
      frontend/src/app/features/content/shared/forms/field-editor.component.html
  5. 2
      frontend/src/app/features/rules/shared/actions/formattable-input.component.html
  6. 2
      frontend/src/app/features/rules/shared/actions/generic-action.component.html
  7. 2
      frontend/src/app/features/schemas/pages/schema/fields/types/json-more.component.html
  8. 2
      frontend/src/app/features/schemas/pages/schemas/schema-form.component.html
  9. 2
      frontend/src/app/framework/angular/forms/editors/code-editor.component.ts
  10. 4
      frontend/src/app/framework/angular/forms/form-alert.component.ts
  11. 2
      frontend/src/app/shared/components/tour-hint.directive.ts
  12. 1
      frontend/src/app/theme/_lists.scss

4
backend/src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs

@ -117,7 +117,7 @@ public sealed class BackgroundUsageTracker : DisposableObjectBase, IUsageTracker
category = GetCategory(category); category = GetCategory(category);
#pragma warning disable MA0105 // Use the lambda parameters instead of using a closure #pragma warning disable MA0105 // Use the lambda parameters instead of using a closure
jobs.AddOrUpdate((key, category, date), counters, (k, p) => p.SumUp(counters)); jobs.AddOrUpdate((key, category, date), counters, (k, p) => p.SumpUpCloned(counters));
#pragma warning restore MA0105 // Use the lambda parameters instead of using a closure #pragma warning restore MA0105 // Use the lambda parameters instead of using a closure
return Task.CompletedTask; return Task.CompletedTask;
@ -191,7 +191,7 @@ public sealed class BackgroundUsageTracker : DisposableObjectBase, IUsageTracker
foreach (var usage in queried) foreach (var usage in queried)
{ {
result.SumUp(usage.Counters); result.SumpUp(usage.Counters);
} }
return result; return result;

15
backend/src/Squidex.Infrastructure/UsageTracking/Counters.cs

@ -42,15 +42,22 @@ public sealed class Counters : Dictionary<string, double>
return (long)value; return (long)value;
} }
public Counters SumUp(Counters counters) public Counters SumpUpCloned(Counters counters)
{ {
foreach (var (key, value) in counters) var result = new Counters(this);
return result.SumpUp(counters);
}
public Counters SumpUp(Counters source)
{
foreach (var (key, value) in source)
{ {
var newValue = value; var newValue = value;
if (TryGetValue(key, out var temp)) if (TryGetValue(key, out var existing))
{ {
newValue += temp; newValue += existing;
} }
this[key] = newValue; this[key] = newValue;

34
backend/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs

@ -243,6 +243,40 @@ public class BackgroundUsageTrackerTests
.MustHaveHappened(); .MustHaveHappened();
} }
[Fact]
public async Task Should_write_with_shared_counters()
{
var key1 = Guid.NewGuid().ToString();
var key2 = Guid.NewGuid().ToString();
var counters1 = Counters(a: 1, b: 2);
await sut.TrackAsync(date, key1, "my-category", counters1, ct);
await sut.TrackAsync(date, key2, "my-category", counters1, ct);
await sut.TrackAsync(date, key2, "my-category", Counters(a: 0.3, b: 2000), ct);
UsageUpdate[]? updates = null;
A.CallTo(() => usageStore.TrackUsagesAsync(A<UsageUpdate[]>._, A<CancellationToken>._))
.Invokes(args =>
{
updates = args.GetArgument<UsageUpdate[]>(0)!;
});
// Wait for the timer to trigger.
await WaitForCompletion();
updates.Should().BeEquivalentTo(new[]
{
new UsageUpdate(date, key1, "my-category", Counters(a: 1.0, b: 2)),
new UsageUpdate(date, key2, "my-category", Counters(a: 1.3, b: 2002)),
}, o => o.ComparingByMembers<UsageUpdate>());
A.CallTo(() => usageStore.TrackUsagesAsync(A<UsageUpdate[]>._, A<CancellationToken>._))
.MustHaveHappened();
}
private async Task WaitForCompletion() private async Task WaitForCompletion()
{ {
sut.Next(); sut.Next();

4
frontend/src/app/features/content/shared/forms/field-editor.component.html

@ -106,7 +106,7 @@
<sqx-geolocation-editor [formControl]="$any(fieldForm)"></sqx-geolocation-editor> <sqx-geolocation-editor [formControl]="$any(fieldForm)"></sqx-geolocation-editor>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'Json'"> <ng-container *ngSwitchCase="'Json'">
<sqx-code-editor [formControl]="$any(fieldForm)" valueMode="Json" height="350"></sqx-code-editor> <sqx-code-editor [formControl]="$any(fieldForm)" valueMode="Json" [height]="350"></sqx-code-editor>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'Number'"> <ng-container *ngSwitchCase="'Number'">
<ng-container [ngSwitch]="field.rawProperties.editor"> <ng-container [ngSwitch]="field.rawProperties.editor">
@ -195,7 +195,7 @@
</sqx-rich-editor> </sqx-rich-editor>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'Html'"> <ng-container *ngSwitchCase="'Html'">
<sqx-code-editor [formControl]="$any(fieldForm)" #editor mode="ace/mode/html" height="350" ></sqx-code-editor> <sqx-code-editor [formControl]="$any(fieldForm)" #editor mode="ace/mode/html" [height]="350" ></sqx-code-editor>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'Markdown'"> <ng-container *ngSwitchCase="'Markdown'">
<sqx-markdown-editor [formControl]="$any(fieldForm)" #editor <sqx-markdown-editor [formControl]="$any(fieldForm)" #editor

2
frontend/src/app/features/rules/shared/actions/formattable-input.component.html

@ -17,5 +17,5 @@
</button> </button>
</div> </div>
<sqx-code-editor [completion]="editorCompletion" height="350" [mode]="editorMode" [snippets]="mode === 'Script'"></sqx-code-editor> <sqx-code-editor [completion]="editorCompletion" [height]="350" [mode]="editorMode" [snippets]="mode === 'Script'"></sqx-code-editor>
</ng-template> </ng-template>

2
frontend/src/app/features/rules/shared/actions/generic-action.component.html

@ -25,7 +25,7 @@
</ng-template> </ng-template>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'Javascript'"> <ng-container *ngSwitchCase="'Javascript'">
<sqx-code-editor [completion]="ruleCompletions | async" [formControlName]="property.name" height="350"></sqx-code-editor> <sqx-code-editor [completion]="ruleCompletions | async" [formControlName]="property.name" [height]="350"></sqx-code-editor>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'Checkbox'"> <ng-container *ngSwitchCase="'Checkbox'">
<div class="form-check"> <div class="form-check">

2
frontend/src/app/features/schemas/pages/schema/fields/types/json-more.component.html

@ -2,7 +2,7 @@
<div class="form-group"> <div class="form-group">
<label>{{ 'schemas.field.graphQLSchema' | sqxTranslate }}</label> <label>{{ 'schemas.field.graphQLSchema' | sqxTranslate }}</label>
<sqx-code-editor formControlName="graphQLSchema" mode="ace/mode/graphqlschema" height="350"></sqx-code-editor> <sqx-code-editor formControlName="graphQLSchema" mode="ace/mode/graphqlschema" [height]="350"></sqx-code-editor>
<sqx-form-hint> <sqx-form-hint>
{{ 'schemas.field.graphQLSchemaHint' | sqxTranslate }} {{ 'schemas.field.graphQLSchemaHint' | sqxTranslate }}

2
frontend/src/app/features/schemas/pages/schemas/schema-form.component.html

@ -105,7 +105,7 @@
{{ 'common.hide' | sqxTranslate }} {{ 'common.hide' | sqxTranslate }}
</button> </button>
<sqx-code-editor height="250" *ngIf="showImport" formControlName="importing" valueMode="Json"></sqx-code-editor> <sqx-code-editor [height]="250" *ngIf="showImport" formControlName="importing" valueMode="Json"></sqx-code-editor>
</div> </div>
</ng-container> </ng-container>

2
frontend/src/app/framework/angular/forms/editors/code-editor.component.ts

@ -61,7 +61,7 @@ export class CodeEditorComponent extends StatefulControlComponent<{}, any> imple
@Input({ transform: booleanAttribute }) @Input({ transform: booleanAttribute })
public wordWrap = false; public wordWrap = false;
@Input({ transform: numberAttribute }) @Input()
public height: number | 'auto' | 'full' = 'full'; public height: number | 'auto' | 'full' = 'full';
@Input({ transform: booleanAttribute }) @Input({ transform: booleanAttribute })

4
frontend/src/app/framework/angular/forms/form-alert.component.ts

@ -18,10 +18,10 @@ export class FormAlertComponent {
public class = ''; public class = '';
@Input({ transform: numberAttribute }) @Input({ transform: numberAttribute })
public marginTop: number | string | undefined | null = 2; public marginTop: number | undefined | null = 2;
@Input({ transform: numberAttribute }) @Input({ transform: numberAttribute })
public marginBottom: number | string | undefined | null = 4; public marginBottom: number | undefined | null = 4;
@Input({ transform: booleanAttribute }) @Input({ transform: booleanAttribute })
public light?: boolean | null; public light?: boolean | null;

2
frontend/src/app/shared/components/tour-hint.directive.ts

@ -20,7 +20,7 @@ export class TourHintDirective extends ResourceOwner implements OnDestroy, OnIni
public hintText!: string; public hintText!: string;
@Input({ transform: numberAttribute }) @Input({ transform: numberAttribute })
public hintAfter: number | string = 1000; public hintAfter: number = 1000;
@Input() @Input()
public hintPosition?: FloatingPlacement; public hintPosition?: FloatingPlacement;

1
frontend/src/app/theme/_lists.scss

@ -38,6 +38,7 @@
thead { thead {
// Small font size for the table header, content is more important! // Small font size for the table header, content is more important!
th { th {
background: none;
border: 0; border: 0;
color: $color-text-decent; color: $color-text-decent;
font-size: .8rem; font-size: .8rem;

Loading…
Cancel
Save