Browse Source

HATEOS for event consumers.

pull/363/head
Sebastian 7 years ago
parent
commit
368cfc7b82
  1. 29
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs
  2. 12
      src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs
  3. 6
      src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerGrain.cs
  4. 10
      src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerManagerGrain.cs
  5. 1
      src/Squidex.Web/Resource.cs
  6. 2
      src/Squidex.Web/UrlHelperExtensions.cs
  7. 1
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  8. 31
      src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs
  9. 37
      src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumerDto.cs
  10. 39
      src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumersDto.cs
  11. 6
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html
  12. 122
      src/Squidex/app/features/administration/services/event-consumers.service.spec.ts
  13. 80
      src/Squidex/app/features/administration/services/event-consumers.service.ts
  14. 3
      src/Squidex/app/features/administration/services/users.service.spec.ts
  15. 60
      src/Squidex/app/features/administration/state/event-consumers.state.spec.ts
  16. 19
      src/Squidex/app/features/administration/state/event-consumers.state.ts
  17. 10
      src/Squidex/app/features/administration/state/users.state.spec.ts

29
src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerGrain.cs

@ -58,7 +58,12 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
public Task<Immutable<EventConsumerInfo>> GetStateAsync()
{
return Task.FromResult(State.ToInfo(eventConsumer.Name).AsImmutable());
return Task.FromResult(CreateInfo());
}
private Immutable<EventConsumerInfo> CreateInfo()
{
return State.ToInfo(eventConsumer.Name).AsImmutable();
}
public Task OnEventAsync(Immutable<IEventSubscription> subscription, Immutable<StoredEvent> storedEvent)
@ -109,39 +114,43 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
return TaskHelper.Done;
}
public Task StartAsync()
public async Task<Immutable<EventConsumerInfo>> StartAsync()
{
if (!State.IsStopped)
{
return TaskHelper.Done;
return CreateInfo();
}
return DoAndUpdateStateAsync(() =>
await DoAndUpdateStateAsync(() =>
{
Subscribe(State.Position);
State = State.Started();
});
return CreateInfo();
}
public Task StopAsync()
public async Task<Immutable<EventConsumerInfo>> StopAsync()
{
if (State.IsStopped)
{
return TaskHelper.Done;
return CreateInfo();
}
return DoAndUpdateStateAsync(() =>
await DoAndUpdateStateAsync(() =>
{
Unsubscribe();
State = State.Stopped();
});
return CreateInfo();
}
public Task ResetAsync()
public async Task<Immutable<EventConsumerInfo>> ResetAsync()
{
return DoAndUpdateStateAsync(async () =>
await DoAndUpdateStateAsync(async () =>
{
Unsubscribe();
@ -151,6 +160,8 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
State = State.Reset();
});
return CreateInfo();
}
private Task DoAndUpdateStateAsync(Action action, [CallerMemberName] string caller = null)

12
src/Squidex.Infrastructure/EventSourcing/Grains/EventConsumerManagerGrain.cs

@ -74,33 +74,31 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{
return Task.WhenAll(
eventConsumers
.Select(c => GrainFactory.GetGrain<IEventConsumerGrain>(c.Name))
.Select(c => c.StartAsync()));
.Select(c => StartAsync(c.Name)));
}
public Task StopAllAsync()
{
return Task.WhenAll(
eventConsumers
.Select(c => GrainFactory.GetGrain<IEventConsumerGrain>(c.Name))
.Select(c => c.StopAsync()));
.Select(c => StopAsync(c.Name)));
}
public Task ResetAsync(string consumerName)
public Task<Immutable<EventConsumerInfo>> ResetAsync(string consumerName)
{
var eventConsumer = GrainFactory.GetGrain<IEventConsumerGrain>(consumerName);
return eventConsumer.ResetAsync();
}
public Task StartAsync(string consumerName)
public Task<Immutable<EventConsumerInfo>> StartAsync(string consumerName)
{
var eventConsumer = GrainFactory.GetGrain<IEventConsumerGrain>(consumerName);
return eventConsumer.StartAsync();
}
public Task StopAsync(string consumerName)
public Task<Immutable<EventConsumerInfo>> StopAsync(string consumerName)
{
var eventConsumer = GrainFactory.GetGrain<IEventConsumerGrain>(consumerName);

6
src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerGrain.cs

@ -16,11 +16,11 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{
Task<Immutable<EventConsumerInfo>> GetStateAsync();
Task StopAsync();
Task<Immutable<EventConsumerInfo>> StopAsync();
Task StartAsync();
Task<Immutable<EventConsumerInfo>> StartAsync();
Task ResetAsync();
Task<Immutable<EventConsumerInfo>> ResetAsync();
Task OnEventAsync(Immutable<IEventSubscription> subscription, Immutable<StoredEvent> storedEvent);

10
src/Squidex.Infrastructure/EventSourcing/Grains/IEventConsumerManagerGrain.cs

@ -16,15 +16,15 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{
Task ActivateAsync(string streamName);
Task StopAllAsync();
Task StartAllAsync();
Task StopAsync(string consumerName);
Task StopAllAsync();
Task StartAllAsync();
Task<Immutable<EventConsumerInfo>> StopAsync(string consumerName);
Task StartAsync(string consumerName);
Task<Immutable<EventConsumerInfo>> StartAsync(string consumerName);
Task ResetAsync(string consumerName);
Task<Immutable<EventConsumerInfo>> ResetAsync(string consumerName);
Task<Immutable<List<EventConsumerInfo>>> GetConsumersAsync();
}

1
src/Squidex.Web/Resource.cs

@ -8,7 +8,6 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Http;
namespace Squidex.Web
{

2
src/Squidex.Web/UrlHelperExtensions.cs

@ -7,8 +7,6 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Squidex.Web
{

1
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -21,7 +21,6 @@ using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.GraphQL;
using Squidex.Infrastructure.Commands;
using Squidex.Shared;
using Squidex.Shared.Identity;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents

31
src/Squidex/Areas/Api/Controllers/EventConsumers/EventConsumersController.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Orleans;
@ -30,44 +29,54 @@ namespace Squidex.Areas.Api.Controllers.EventConsumers
[HttpGet]
[Route("event-consumers/")]
[ProducesResponseType(typeof(EventConsumersDto), 200)]
[ApiPermission(Permissions.AdminEventsRead)]
public async Task<IActionResult> GetEventConsumers()
{
var entities = await GetGrain().GetConsumersAsync();
var response = entities.Value.OrderBy(x => x.Name).Select(EventConsumerDto.FromEventConsumerInfo).ToArray();
var response = EventConsumersDto.FromResults(entities.Value, this);
return Ok(response);
}
[HttpPut]
[Route("event-consumers/{name}/start/")]
[ProducesResponseType(typeof(EventConsumerDto), 200)]
[ApiPermission(Permissions.AdminEventsManage)]
public async Task<IActionResult> Start(string name)
public async Task<IActionResult> StartEventConsumer(string name)
{
await GetGrain().StartAsync(name);
var entity = await GetGrain().StartAsync(name);
return NoContent();
var response = EventConsumerDto.FromEventConsumerInfo(entity.Value, this);
return Ok(response);
}
[HttpPut]
[Route("event-consumers/{name}/stop/")]
[ProducesResponseType(typeof(EventConsumerDto), 200)]
[ApiPermission(Permissions.AdminEventsManage)]
public async Task<IActionResult> Stop(string name)
public async Task<IActionResult> StopEventConsumer(string name)
{
await GetGrain().StopAsync(name);
var entity = await GetGrain().StopAsync(name);
var response = EventConsumerDto.FromEventConsumerInfo(entity.Value, this);
return NoContent();
return Ok(response);
}
[HttpPut]
[Route("event-consumers/{name}/reset/")]
[ProducesResponseType(typeof(EventConsumerDto), 200)]
[ApiPermission(Permissions.AdminEventsManage)]
public async Task<IActionResult> Reset(string name)
public async Task<IActionResult> ResetEventConsumer(string name)
{
await GetGrain().ResetAsync(name);
var entity = await GetGrain().ResetAsync(name);
var response = EventConsumerDto.FromEventConsumerInfo(entity.Value, this);
return NoContent();
return Ok(response);
}
private IEventConsumerManagerGrain GetGrain()

37
src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumerDto.cs

@ -7,11 +7,16 @@
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security;
using Squidex.Shared;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.EventConsumers.Models
{
public sealed class EventConsumerDto
public sealed class EventConsumerDto : Resource
{
private static readonly Permission EventsManagePermission = new Permission(Permissions.AdminEventsManage);
public bool IsStopped { get; set; }
public bool IsResetting { get; set; }
@ -22,9 +27,35 @@ namespace Squidex.Areas.Api.Controllers.EventConsumers.Models
public string Position { get; set; }
public static EventConsumerDto FromEventConsumerInfo(EventConsumerInfo eventConsumerInfo)
public static EventConsumerDto FromEventConsumerInfo(EventConsumerInfo eventConsumerInfo, ApiController controller)
{
var result = SimpleMapper.Map(eventConsumerInfo, new EventConsumerDto());
return CreateLinks(result, controller);
}
private static EventConsumerDto CreateLinks(EventConsumerDto result, ApiController controller)
{
return SimpleMapper.Map(eventConsumerInfo, new EventConsumerDto());
if (controller.HasPermission(EventsManagePermission))
{
var values = new { name = result.Name };
if (!result.IsResetting)
{
result.AddPutLink("reset", controller.Url<EventConsumersController>(x => nameof(x.ResetEventConsumer), values));
}
if (result.IsStopped)
{
result.AddPutLink("start", controller.Url<EventConsumersController>(x => nameof(x.StartEventConsumer), values));
}
else
{
result.AddPutLink("stop", controller.Url<EventConsumersController>(x => nameof(x.StopEventConsumer), values));
}
}
return result;
}
}
}

39
src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumersDto.cs

@ -0,0 +1,39 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.EventConsumers.Models
{
public sealed class EventConsumersDto : Resource
{
/// <summary>
/// The event consumers.
/// </summary>
public EventConsumerDto[] Items { get; set; }
public static EventConsumersDto FromResults(IEnumerable<EventConsumerInfo> items, ApiController controller)
{
var result = new EventConsumersDto
{
Items = items.Select(x => EventConsumerDto.FromEventConsumerInfo(x, controller)).ToArray()
};
return CreateLinks(result, controller);
}
private static EventConsumersDto CreateLinks(EventConsumersDto result, ApiController controller)
{
result.AddSelfLink(controller.Url<EventConsumersController>(c => nameof(c.GetEventConsumers)));
return result;
}
}
}

6
src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html

@ -42,13 +42,13 @@
<span>{{eventConsumer.position}}</span>
</td>
<td class="cell-actions-lg">
<button type="button" class="btn btn-text" (click)="reset(eventConsumer)" *ngIf="!eventConsumer.isResetting" title="Reset Event Consumer">
<button type="button" class="btn btn-text" (click)="reset(eventConsumer)" *ngIf="eventConsumer | sqxHasLink:'reset'" title="Reset Event Consumer">
<i class="icon icon-reset"></i>
</button>
<button type="button" class="btn btn-text" (click)="start(eventConsumer)" *ngIf="eventConsumer.isStopped" title="Start Event Consumer">
<button type="button" class="btn btn-text" (click)="start(eventConsumer)" *ngIf="eventConsumer | sqxHasLink:'start'" title="Start Event Consumer">
<i class="icon icon-play"></i>
</button>
<button type="button" class="btn btn-text" (click)="stop(eventConsumer)" *ngIf="!eventConsumer.isStopped" title="Stop Event Consumer">
<button type="button" class="btn btn-text" (click)="stop(eventConsumer)" *ngIf="eventConsumer | sqxHasLink:'stop'" title="Stop Event Consumer">
<i class="icon icon-pause"></i>
</button>
</td>

122
src/Squidex/app/features/administration/services/event-consumers.service.spec.ts

@ -8,9 +8,13 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing';
import { ApiUrlConfig } from '@app/framework';
import { ApiUrlConfig, Resource } from '@app/framework';
import { EventConsumerDto, EventConsumersService } from './event-consumers.service';
import {
EventConsumerDto,
EventConsumersDto,
EventConsumersService
} from './event-consumers.service';
describe('EventConsumersService', () => {
beforeEach(() => {
@ -32,7 +36,7 @@ describe('EventConsumersService', () => {
it('should make get request to get event consumers',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
let eventConsumers: EventConsumerDto[];
let eventConsumers: EventConsumersDto;
eventConsumersService.getEventConsumers().subscribe(result => {
eventConsumers = result;
@ -43,66 +47,120 @@ describe('EventConsumersService', () => {
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush([
{
name: 'event-consumer1',
position: '13',
isStopped: true,
isResetting: true,
error: 'an error 1'
},
{
name: 'event-consumer2',
position: '29',
isStopped: true,
isResetting: true,
error: 'an error 2'
}
]);
req.flush({
items: [
eventConsumerResponse(12),
eventConsumerResponse(13)
]
});
expect(eventConsumers!).toEqual(
[
new EventConsumerDto('event-consumer1', true, true, 'an error 1', '13'),
new EventConsumerDto('event-consumer2', true, true, 'an error 2', '29')
]);
new EventConsumersDto([
createEventConsumer(12),
createEventConsumer(13)
]));
}));
it('should make put request to start event consumer',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
eventConsumersService.putStart('event-consumer1').subscribe();
const resource: Resource = {
_links: {
start: { method: 'PUT', href: 'api/event-consumers/event-consumer123/start' }
}
};
let eventConsumer: EventConsumerDto;
eventConsumersService.putStart(resource).subscribe(response => {
eventConsumer = response;
});
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer1/start');
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer123/start');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
req.flush(eventConsumerResponse(123));
expect(eventConsumer!).toEqual(createEventConsumer(123));
}));
it('should make put request to stop event consumer',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
eventConsumersService.putStop('event-consumer1').subscribe();
const resource: Resource = {
_links: {
stop: { method: 'PUT', href: 'api/event-consumers/event-consumer123/stop' }
}
};
let eventConsumer: EventConsumerDto;
eventConsumersService.putStop(resource).subscribe(response => {
eventConsumer = response;
});
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer1/stop');
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer123/stop');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
req.flush(eventConsumerResponse(12));
expect(eventConsumer!).toEqual(createEventConsumer(12));
}));
it('should make put request to reset event consumer',
inject([EventConsumersService, HttpTestingController], (eventConsumersService: EventConsumersService, httpMock: HttpTestingController) => {
eventConsumersService.putReset('event-consumer1').subscribe();
const resource: Resource = {
_links: {
reset: { method: 'PUT', href: 'api/event-consumers/event-consumer123/reset' }
}
};
let eventConsumer: EventConsumerDto;
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer1/reset');
eventConsumersService.putReset(resource).subscribe(response => {
eventConsumer = response;
});
const req = httpMock.expectOne('http://service/p/api/event-consumers/event-consumer123/reset');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush({});
req.flush(eventConsumerResponse(12));
expect(eventConsumer!).toEqual(createEventConsumer(12));
}));
function eventConsumerResponse(id: number) {
return {
name: `event-consumer${id}`,
position: `position-${id}`,
isStopped: true,
isResetting: true,
error: `failure-${id}`,
_links: {
reset: { method: 'PUT', href: `/event-consumers/${id}/reset` }
}
};
}
});
export function createEventConsumer(id: number, suffix = '') {
const result = new EventConsumerDto(
`event-consumer${id}`,
true,
true,
`failure-${id}${suffix}`,
`position-${id}${suffix}`);
result._links['reset'] = {
method: 'PUT', href: `/event-consumers/${id}/reset`
};
return result;
}

80
src/Squidex/app/features/administration/services/event-consumers.service.ts

@ -12,11 +12,24 @@ import { map } from 'rxjs/operators';
import {
ApiUrlConfig,
Model,
pretifyError
pretifyError,
Resource,
ResourceLinks,
withLinks
} from '@app/shared';
export class EventConsumerDto extends Model<EventConsumerDto> {
export class EventConsumersDto {
public readonly _links: ResourceLinks = {};
constructor(
public readonly items: EventConsumerDto[]
) {
}
}
export class EventConsumerDto {
public readonly _links: ResourceLinks = {};
constructor(
public readonly name: string,
public readonly isStopped?: boolean,
@ -24,7 +37,6 @@ export class EventConsumerDto extends Model<EventConsumerDto> {
public readonly error?: string,
public readonly position?: string
) {
super();
}
}
@ -36,42 +48,62 @@ export class EventConsumersService {
) {
}
public getEventConsumers(): Observable<EventConsumerDto[]> {
public getEventConsumers(): Observable<EventConsumersDto> {
const url = this.apiUrl.buildUrl('/api/event-consumers');
return this.http.get<any[]>(url).pipe(
return this.http.get<{ items: any[] } & Resource>(url).pipe(
map(body => {
const eventConsumers = body.map(item =>
new EventConsumerDto(
item.name,
item.isStopped,
item.isResetting,
item.error,
item.position));
return eventConsumers;
const eventConsumers = body.items.map(item => parseEventConsumer(item));
return withLinks(new EventConsumersDto(eventConsumers), body);
}),
pretifyError('Failed to load event consumers. Please reload.'));
}
public putStart(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/start`);
public putStart(eventConsumer: Resource): Observable<EventConsumerDto> {
const link = eventConsumer._links['start'];
return this.http.put(url, {}).pipe(
const url = this.apiUrl.buildUrl(link.href);
return this.http.request(link.method, url).pipe(
map(body => {
return parseEventConsumer(body);
}),
pretifyError('Failed to start event consumer. Please reload.'));
}
public putStop(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/stop`);
public putStop(eventConsumer: Resource): Observable<EventConsumerDto> {
const link = eventConsumer._links['stop'];
const url = this.apiUrl.buildUrl(link.href);
return this.http.put(url, {}).pipe(
return this.http.request(link.method, url).pipe(
map(body => {
return parseEventConsumer(body);
}),
pretifyError('Failed to stop event consumer. Please reload.'));
}
public putReset(name: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/event-consumers/${name}/reset`);
public putReset(eventConsumer: Resource): Observable<EventConsumerDto> {
const link = eventConsumer._links['reset'];
const url = this.apiUrl.buildUrl(link.href);
return this.http.put(url, {}).pipe(
return this.http.request(link.method, url).pipe(
map(body => {
return parseEventConsumer(body);
}),
pretifyError('Failed to reset event consumer. Please reload.'));
}
}
function parseEventConsumer(response: any): EventConsumerDto {
return withLinks(
new EventConsumerDto(
response.name,
response.isStopped,
response.isResetting,
response.error,
response.position),
response);
}

3
src/Squidex/app/features/administration/services/users.service.spec.ts

@ -227,7 +227,8 @@ describe('UsersService', () => {
});
export function createUser(id: number, suffix = '') {
const result = new UserDto(`${id}`,
const result = new UserDto(
`${id}`,
`user${id}${suffix}@domain.com`,
`user${id}${suffix}`,
[

60
src/Squidex/app/features/administration/state/event-consumers.state.spec.ts

@ -11,14 +11,14 @@ import { IMock, It, Mock, Times } from 'typemoq';
import { DialogService } from '@app/framework';
import { EventConsumerDto, EventConsumersService } from '@app/features/administration/internal';
import { EventConsumersDto, EventConsumersService } from '@app/features/administration/internal';
import { EventConsumersState } from './event-consumers.state';
import { createEventConsumer } from './../services/event-consumers.service.spec';
describe('EventConsumersState', () => {
const oldConsumers = [
new EventConsumerDto('name1', false, false, 'error', '1'),
new EventConsumerDto('name2', true, true, 'error', '2')
];
const eventConsumer1 = createEventConsumer(1);
const eventConsumer2 = createEventConsumer(2);
let dialogs: IMock<DialogService>;
let eventConsumersService: IMock<EventConsumersService>;
@ -38,11 +38,11 @@ describe('EventConsumersState', () => {
describe('Loading', () => {
it('should load event consumers', () => {
eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => of(oldConsumers)).verifiable();
.returns(() => of(new EventConsumersDto([eventConsumer1, eventConsumer2]))).verifiable();
eventConsumersState.load().subscribe();
expect(eventConsumersState.snapshot.eventConsumers.values).toEqual(oldConsumers);
expect(eventConsumersState.snapshot.eventConsumers.values).toEqual([eventConsumer1, eventConsumer2]);
expect(eventConsumersState.snapshot.isLoaded).toBeTruthy();
dialogs.verify(x => x.notifyInfo(It.isAnyString()), Times.never());
@ -50,7 +50,7 @@ describe('EventConsumersState', () => {
it('should show notification on load when reload is true', () => {
eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => of(oldConsumers)).verifiable();
.returns(() => of(new EventConsumersDto([eventConsumer1, eventConsumer2]))).verifiable();
eventConsumersState.load(true).subscribe();
@ -74,42 +74,48 @@ describe('EventConsumersState', () => {
describe('Updates', () => {
beforeEach(() => {
eventConsumersService.setup(x => x.getEventConsumers())
.returns(() => of(oldConsumers)).verifiable();
.returns(() => of(new EventConsumersDto([eventConsumer1, eventConsumer2]))).verifiable();
eventConsumersState.load().subscribe();
});
it('should unmark as stopped when started', () => {
eventConsumersService.setup(x => x.putStart(oldConsumers[1].name))
.returns(() => of({})).verifiable();
it('should update evnet consumer when started', () => {
const updated = createEventConsumer(2, '_new');
eventConsumersService.setup(x => x.putStart(eventConsumer2))
.returns(() => of(updated)).verifiable();
eventConsumersState.start(oldConsumers[1]).subscribe();
eventConsumersState.start(eventConsumer2).subscribe();
const es_1 = eventConsumersState.snapshot.eventConsumers.at(1);
const newConsumer2 = eventConsumersState.snapshot.eventConsumers.at(1);
expect(es_1.isStopped).toBeFalsy();
expect(newConsumer2).toEqual(updated);
});
it('should mark as stopped when stopped', () => {
eventConsumersService.setup(x => x.putStop(oldConsumers[0].name))
.returns(() => of({})).verifiable();
it('should update event consumer when stopped', () => {
const updated = createEventConsumer(2, '_new');
eventConsumersState.stop(oldConsumers[0]).subscribe();
eventConsumersService.setup(x => x.putStop(eventConsumer2))
.returns(() => of(updated)).verifiable();
const es_1 = eventConsumersState.snapshot.eventConsumers.at(0);
eventConsumersState.stop(eventConsumer2).subscribe();
expect(es_1.isStopped).toBeTruthy();
const newConsumer2 = eventConsumersState.snapshot.eventConsumers.at(1);
expect(newConsumer2).toEqual(updated);
});
it('should mark as resetting when reset', () => {
eventConsumersService.setup(x => x.putReset(oldConsumers[0].name))
.returns(() => of({})).verifiable();
it('should update event consumer when reset', () => {
const updated = createEventConsumer(2, '_new');
eventConsumersService.setup(x => x.putReset(eventConsumer2))
.returns(() => of(updated)).verifiable();
eventConsumersState.reset(oldConsumers[0]).subscribe();
eventConsumersState.reset(eventConsumer2).subscribe();
const es_1 = eventConsumersState.snapshot.eventConsumers.at(0);
const newConsumer2 = eventConsumersState.snapshot.eventConsumers.at(1);
expect(es_1.isResetting).toBeTruthy();
expect(newConsumer2).toEqual(updated);
});
});
});

19
src/Squidex/app/features/administration/state/event-consumers.state.ts

@ -51,12 +51,12 @@ export class EventConsumersState extends State<Snapshot> {
}
return this.eventConsumersService.getEventConsumers().pipe(
tap(payload => {
tap(({ items }) => {
if (isReload && !silent) {
this.dialogs.notifyInfo('Event Consumers reloaded.');
}
const eventConsumers = ImmutableArray.of(payload);
const eventConsumers = ImmutableArray.of(items);
this.next(s => {
return { ...s, eventConsumers, isLoaded: true };
@ -66,8 +66,7 @@ export class EventConsumersState extends State<Snapshot> {
}
public start(eventConsumer: EventConsumerDto): Observable<any> {
return this.eventConsumersService.putStart(eventConsumer.name).pipe(
map(() => setStopped(eventConsumer, false)),
return this.eventConsumersService.putStart(eventConsumer).pipe(
tap(updated => {
this.replaceEventConsumer(updated);
}),
@ -75,8 +74,7 @@ export class EventConsumersState extends State<Snapshot> {
}
public stop(eventConsumer: EventConsumerDto): Observable<EventConsumerDto> {
return this.eventConsumersService.putStop(eventConsumer.name).pipe(
map(() => setStopped(eventConsumer, true)),
return this.eventConsumersService.putStop(eventConsumer).pipe(
tap(updated => {
this.replaceEventConsumer(updated);
}),
@ -84,8 +82,7 @@ export class EventConsumersState extends State<Snapshot> {
}
public reset(eventConsumer: EventConsumerDto): Observable<EventConsumerDto> {
return this.eventConsumersService.putReset(eventConsumer.name).pipe(
map(() => reset(eventConsumer)),
return this.eventConsumersService.putReset(eventConsumer).pipe(
tap(updated => {
this.replaceEventConsumer(updated);
}),
@ -100,9 +97,3 @@ export class EventConsumersState extends State<Snapshot> {
});
}
}
const setStopped = (eventConsumer: EventConsumerDto, isStopped: boolean) =>
eventConsumer.with({ isStopped });
const reset = (eventConsumer: EventConsumerDto) =>
eventConsumer.with({ isResetting: true });

10
src/Squidex/app/features/administration/state/users.state.spec.ts

@ -168,7 +168,7 @@ describe('UsersState', () => {
expect(usersState.snapshot.selectedUser).toBeNull();
});
it('should mark as locked when locked', () => {
it('should update user selected user when locked', () => {
const updated = createUser(2, '_new');
usersService.setup(x => x.lockUser(user2))
@ -177,12 +177,12 @@ describe('UsersState', () => {
usersState.select(user2.id).subscribe();
usersState.lock(user2).subscribe();
const userUser2 = usersState.snapshot.users.at(1);
const newUser2 = usersState.snapshot.users.at(1);
expect(userUser2).toBe(usersState.snapshot.selectedUser!);
expect(newUser2).toBe(usersState.snapshot.selectedUser!);
});
it('should unmark as locked when unlocked', () => {
it('should update user and selected user when unlocked', () => {
const updated = createUser(2, '_new');
usersService.setup(x => x.unlockUser(user2))
@ -197,7 +197,7 @@ describe('UsersState', () => {
expect(newUser2).toBe(usersState.snapshot.selectedUser!);
});
it('should update user properties when updated', () => {
it('should update user and selected user when updated', () => {
const request = { email: 'new@mail.com', displayName: 'New', permissions: ['Permission1'] };
const updated = createUser(2, '_new');

Loading…
Cancel
Save