Browse Source

Webhook Commands and Handling

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
8707b85c1d
  1. 23
      src/Squidex.Events/Schemas/WebhookAdded.cs
  2. 19
      src/Squidex.Events/Schemas/WebhookDeleted.cs
  3. 28
      src/Squidex.Infrastructure/RandomHash.cs
  4. 13
      src/Squidex.Write/Apps/AppCommandHandler.cs
  5. 4
      src/Squidex.Write/Apps/AppDomainObject.cs
  6. 42
      src/Squidex.Write/Apps/ClientKeyGenerator.cs
  7. 2
      src/Squidex.Write/Apps/Commands/AttachClient.cs
  8. 31
      src/Squidex.Write/Schemas/Commands/AddWebhook.cs
  9. 17
      src/Squidex.Write/Schemas/Commands/DeleteWebhook.cs
  10. 10
      src/Squidex.Write/Schemas/SchemaCommandHandler.cs
  11. 42
      src/Squidex.Write/Schemas/SchemaDomainObject.cs
  12. 8
      src/Squidex/Controllers/Api/Apps/AppClientsController.cs
  13. 8
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  14. 32
      tests/Squidex.Infrastructure.Tests/RandomHashTests.cs
  15. 43
      tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs
  16. 19
      tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs
  17. 25
      tests/Squidex.Write.Tests/Apps/ClientKeyGeneratorTests.cs
  18. 34
      tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs
  19. 95
      tests/Squidex.Write.Tests/Schemas/SchemaDomainObjectTests.cs

23
src/Squidex.Events/Schemas/WebhookAdded.cs

@ -0,0 +1,23 @@
// ==========================================================================
// WebhookAdded.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Events.Schemas
{
[TypeName("WebhookAddedEvent")]
public sealed class WebhookAdded : SchemaEvent
{
public Guid Id { get; set; }
public Uri Url { get; set; }
public string SecurityToken { get; set; }
}
}

19
src/Squidex.Events/Schemas/WebhookDeleted.cs

@ -0,0 +1,19 @@
// ==========================================================================
// WebhookDeleted.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Events.Schemas
{
[TypeName("WebhookDeletedEvent")]
public sealed class WebhookDeleted : SchemaEvent
{
public Guid Id { get; set; }
}
}

28
src/Squidex.Infrastructure/RandomHash.cs

@ -0,0 +1,28 @@
// ==========================================================================
// RandomHash.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Security.Cryptography;
using System.Text;
namespace Squidex.Infrastructure
{
public static class RandomHash
{
public static string New()
{
using (var sha = SHA256.Create())
{
var bytes = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
var hash = sha.ComputeHash(bytes);
return Convert.ToBase64String(hash).Replace("+", "x");
}
}
}
}

13
src/Squidex.Write/Apps/AppCommandHandler.cs

@ -27,25 +27,21 @@ namespace Squidex.Write.Apps
private readonly IAppPlansProvider appPlansProvider;
private readonly IAppPlanBillingManager appPlansBillingManager;
private readonly IUserResolver userResolver;
private readonly ClientKeyGenerator keyGenerator;
public AppCommandHandler(
IAggregateHandler handler,
IAppRepository appRepository,
IAppPlansProvider appPlansProvider,
IAppPlanBillingManager appPlansBillingManager,
IUserResolver userResolver,
ClientKeyGenerator keyGenerator)
IUserResolver userResolver)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(keyGenerator, nameof(keyGenerator));
Guard.NotNull(appRepository, nameof(appRepository));
Guard.NotNull(userResolver, nameof(userResolver));
Guard.NotNull(appPlansProvider, nameof(appPlansProvider));
Guard.NotNull(appPlansBillingManager, nameof(appPlansBillingManager));
this.handler = handler;
this.keyGenerator = keyGenerator;
this.userResolver = userResolver;
this.appRepository = appRepository;
this.appPlansProvider = appPlansProvider;
@ -119,12 +115,7 @@ namespace Squidex.Write.Apps
protected Task On(AttachClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
a.AttachClient(command, keyGenerator.GenerateKey());
context.Succeed(EntityCreatedResult.Create(a.Clients[command.Id], a.Version));
});
return handler.UpdateAsync<AppDomainObject>(context, a => a.AttachClient(command));
}
protected Task On(RemoveContributor command, CommandContext context)

4
src/Squidex.Write/Apps/AppDomainObject.cs

@ -153,13 +153,13 @@ namespace Squidex.Write.Apps
return this;
}
public AppDomainObject AttachClient(AttachClient command, string secret)
public AppDomainObject AttachClient(AttachClient command)
{
Guard.Valid(command, nameof(command), () => "Cannot attach client");
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached { Secret = secret }));
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached()));
return this;
}

42
src/Squidex.Write/Apps/ClientKeyGenerator.cs

@ -1,42 +0,0 @@
// ==========================================================================
// ClientKeyGenerator.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Security.Cryptography;
using System.Text;
// ReSharper disable ClassWithVirtualMembersNeverInherited.Global
namespace Squidex.Write.Apps
{
public class ClientKeyGenerator
{
private readonly Func<HashAlgorithm> algorithmFactory;
public ClientKeyGenerator()
{
algorithmFactory = SHA256.Create;
}
public virtual string GenerateKey()
{
return Hash(Guid.NewGuid().ToString());
}
private string Hash(string input)
{
using (var sha = algorithmFactory())
{
var bytes = Encoding.UTF8.GetBytes(input);
var hash = sha.ComputeHash(bytes);
return Convert.ToBase64String(hash).Replace("+", "x");
}
}
}
}

2
src/Squidex.Write/Apps/Commands/AttachClient.cs

@ -15,6 +15,8 @@ namespace Squidex.Write.Apps.Commands
{
public string Id { get; set; }
public string Secret { get; } = RandomHash.New();
public void Validate(IList<ValidationError> errors)
{
if (!Id.IsSlug())

31
src/Squidex.Write/Schemas/Commands/AddWebhook.cs

@ -0,0 +1,31 @@
// ==========================================================================
// AddWebhook.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Write.Schemas.Commands
{
public sealed class AddWebhook : SchemaAggregateCommand, IValidatable
{
public Guid Id { get; } = Guid.NewGuid();
public Uri Url { get; set; }
public string SecurityToken { get; } = RandomHash.New();
public void Validate(IList<ValidationError> errors)
{
if (Url == null || !Url.IsAbsoluteUri)
{
errors.Add(new ValidationError("Url must be specified and absolute", nameof(Url)));
}
}
}
}

17
src/Squidex.Write/Schemas/Commands/DeleteWebhook.cs

@ -0,0 +1,17 @@
// ==========================================================================
// DeleteWebhook.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Write.Schemas.Commands
{
public class DeleteWebhook : SchemaAggregateCommand
{
public Guid Id { get; set; }
}
}

10
src/Squidex.Write/Schemas/SchemaCommandHandler.cs

@ -60,6 +60,16 @@ namespace Squidex.Write.Schemas
});
}
protected Task On(AddWebhook command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.AddWebhook(command));
}
protected Task On(DeleteWebhook command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.DeleteWebhook(command));
}
protected Task On(DeleteSchema command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.Delete(command));

42
src/Squidex.Write/Schemas/SchemaDomainObject.cs

@ -23,6 +23,7 @@ namespace Squidex.Write.Schemas
public class SchemaDomainObject : DomainObjectBase
{
private readonly FieldRegistry registry;
private readonly HashSet<Guid> webhookIds = new HashSet<Guid>();
private bool isDeleted;
private long totalFields;
private Schema schema;
@ -107,6 +108,16 @@ namespace Squidex.Write.Schemas
schema = SchemaEventDispatcher.Dispatch(@event, schema);
}
protected void On(WebhookAdded @event)
{
webhookIds.Add(@event.Id);
}
protected void On(WebhookDeleted @event)
{
webhookIds.Remove(@event.Id);
}
protected void On(SchemaDeleted @event)
{
isDeleted = true;
@ -137,6 +148,29 @@ namespace Squidex.Write.Schemas
return this;
}
public SchemaDomainObject DeleteWebhook(DeleteWebhook command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
VerifyWebhookExists(command.Id);
RaiseEvent(SimpleMapper.Map(command, new WebhookDeleted()));
return this;
}
public SchemaDomainObject AddWebhook(AddWebhook command)
{
Guard.Valid(command, nameof(command), () => "Cannot add webhook");
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookAdded()));
return this;
}
public SchemaDomainObject AddField(AddField command)
{
Guard.Valid(command, nameof(command), () => $"Cannot add field to schema {Id}");
@ -283,6 +317,14 @@ namespace Squidex.Write.Schemas
RaiseEvent(@event);
}
private void VerifyWebhookExists(Guid id)
{
if (!webhookIds.Contains(id))
{
throw new DomainObjectNotFoundException(id.ToString(), "Webhooks", typeof(Schema));
}
}
private void VerifyNotCreated()
{
if (schema != null)

8
src/Squidex/Controllers/Api/Apps/AppClientsController.cs

@ -15,7 +15,6 @@ using Squidex.Controllers.Api.Apps.Models;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline;
using Squidex.Write.Apps;
using Squidex.Write.Apps.Commands;
namespace Squidex.Controllers.Api.Apps
@ -77,10 +76,11 @@ namespace Squidex.Controllers.Api.Apps
[ApiCosts(1)]
public async Task<IActionResult> PostClient(string app, [FromBody] CreateAppClientDto request)
{
var context = await CommandBus.PublishAsync(SimpleMapper.Map(request, new AttachClient()));
var command = SimpleMapper.Map(request, new AttachClient());
var result = context.Result<EntityCreatedResult<AppClient>>().IdOrValue;
var response = SimpleMapper.Map(result, new ClientDto());
await CommandBus.PublishAsync(command);
var response = SimpleMapper.Map(command, new ClientDto { Name = command .Id });
return CreatedAtAction(nameof(GetClients), new { app }, response);
}

8
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -97,16 +97,16 @@ namespace Squidex.Controllers.ContentApi
return NotFound();
}
var resposne = SimpleMapper.Map(entity, new ContentDto());
var response = SimpleMapper.Map(entity, new ContentDto());
if (entity.Data != null)
{
resposne.Data = entity.Data.ToApiModel(schemaEntity.Schema, App.LanguagesConfig, null, hidden);
response.Data = entity.Data.ToApiModel(schemaEntity.Schema, App.LanguagesConfig, null, hidden);
}
Response.Headers["ETag"] = new StringValues(entity.Version.ToString());
return Ok(resposne);
return Ok(response);
}
[HttpPost]
@ -121,8 +121,6 @@ namespace Squidex.Controllers.ContentApi
var result = context.Result<EntityCreatedResult<ContentData>>();
var response = ContentDto.Create(command, result);
Response.Headers["ETag"] = new StringValues(response.Version.ToString());
return CreatedAtAction(nameof(GetContent), new { id = response.Id }, response);
}

32
tests/Squidex.Infrastructure.Tests/RandomHashTests.cs

@ -0,0 +1,32 @@
// ==========================================================================
// RandomHashTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Xunit;
namespace Squidex.Infrastructure
{
public class RandomHashTests
{
[Fact]
public void Should_create_long_hash()
{
var hash = RandomHash.New();
Assert.Equal(44, hash.Length);
}
[Fact]
public void Should_create_new_hashs()
{
var hash1 = RandomHash.New();
var hash2 = RandomHash.New();
Assert.NotEqual(hash1, hash2);
}
}
}

43
tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs

@ -28,7 +28,6 @@ namespace Squidex.Write.Apps
{
public class AppCommandHandlerTests : HandlerTestBase<AppDomainObject>
{
private readonly Mock<ClientKeyGenerator> keyGenerator = new Mock<ClientKeyGenerator>();
private readonly Mock<IAppRepository> appRepository = new Mock<IAppRepository>();
private readonly Mock<IAppPlansProvider> appPlansProvider = new Mock<IAppPlansProvider>();
private readonly Mock<IAppPlanBillingManager> appPlansBillingManager = new Mock<IAppPlanBillingManager>();
@ -37,14 +36,13 @@ namespace Squidex.Write.Apps
private readonly AppDomainObject app;
private readonly Language language = Language.DE;
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientSecret = Guid.NewGuid().ToString();
private readonly string clientName = "client";
public AppCommandHandlerTests()
{
app = new AppDomainObject(AppId, -1);
sut = new AppCommandHandler(Handler, appRepository.Object, appPlansProvider.Object, appPlansBillingManager.Object, userResolver.Object, keyGenerator.Object);
sut = new AppCommandHandler(Handler, appRepository.Object, appPlansProvider.Object, appPlansBillingManager.Object, userResolver.Object);
}
[Fact]
@ -164,22 +162,14 @@ namespace Squidex.Write.Apps
[Fact]
public async Task AttachClient_should_update_domain_object()
{
keyGenerator.Setup(x => x.GenerateKey())
.Returns(clientSecret)
.Verifiable();
CreateApp();
var context = CreateContextForCommand(new AttachClient { Id = clientName });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
keyGenerator.VerifyAll();
context.Result<EntityCreatedResult<AppClient>>().IdOrValue.ShouldBeEquivalentTo(new AppClient(clientName, clientSecret));
}
[Fact]
@ -188,7 +178,7 @@ namespace Squidex.Write.Apps
appPlansProvider.Setup(x => x.IsConfiguredPlan("my-plan")).Returns(false);
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }), clientSecret);
.AttachClient(CreateCommand(new AttachClient { Id = clientName }));
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" });
@ -199,30 +189,26 @@ namespace Squidex.Write.Apps
}
[Fact]
public async Task ChangePlan_should_update_domain_object()
public async Task RenameClient_should_update_domain_object()
{
appPlansProvider.Setup(x => x.IsConfiguredPlan("my-plan")).Returns(true);
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }), clientSecret);
.AttachClient(CreateCommand(new AttachClient { Id = clientName }));
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" });
var context = CreateContextForCommand(new RenameClient { Id = clientName, Name = "New Name" });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
appPlansBillingManager.Verify(x => x.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan"), Times.Once());
}
[Fact]
public async Task RenameClient_should_update_domain_object()
public async Task RevokeClient_should_update_domain_object()
{
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }), clientSecret);
.AttachClient(CreateCommand(new AttachClient { Id = clientName }));
var context = CreateContextForCommand(new RenameClient { Id = clientName, Name = "New Name" });
var context = CreateContextForCommand(new RevokeClient { Id = clientName });
await TestUpdate(app, async _ =>
{
@ -231,17 +217,20 @@ namespace Squidex.Write.Apps
}
[Fact]
public async Task RevokeClient_should_update_domain_object()
public async Task ChangePlan_should_update_domain_object()
{
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }), clientSecret);
appPlansProvider.Setup(x => x.IsConfiguredPlan("my-plan")).Returns(true);
var context = CreateContextForCommand(new RevokeClient { Id = clientName });
CreateApp();
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
appPlansBillingManager.Verify(x => x.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan"), Times.Once());
}
[Fact]

19
tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs

@ -25,7 +25,6 @@ namespace Squidex.Write.Apps
{
private readonly AppDomainObject sut;
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientSecret = Guid.NewGuid().ToString();
private readonly string clientId = "client";
private readonly string clientNewName = "My Client";
private readonly string planId = "premium";
@ -240,7 +239,7 @@ namespace Squidex.Write.Apps
{
Assert.Throws<DomainException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }), clientSecret);
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
});
}
@ -251,12 +250,12 @@ namespace Squidex.Write.Apps
Assert.Throws<ValidationException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient()), clientSecret);
sut.AttachClient(CreateCommand(new AttachClient()));
});
Assert.Throws<ValidationException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient { Id = string.Empty }), clientSecret);
sut.AttachClient(CreateCommand(new AttachClient { Id = string.Empty }));
});
}
@ -265,24 +264,26 @@ namespace Squidex.Write.Apps
{
CreateApp();
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }), clientSecret);
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
Assert.Throws<ValidationException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }), clientSecret);
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
});
}
[Fact]
public void AttachClient_should_create_events()
{
var command = new AttachClient { Id = clientId };
CreateApp();
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }), clientSecret);
sut.AttachClient(CreateCommand(command));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppClientAttached { Id = clientId, Secret = clientSecret })
CreateEvent(new AppClientAttached { Id = clientId, Secret = command.Secret })
);
}
@ -565,7 +566,7 @@ namespace Squidex.Write.Apps
private void CreateClient()
{
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }), clientSecret);
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
((IAggregate)sut).ClearUncommittedEvents();
}

25
tests/Squidex.Write.Tests/Apps/ClientKeyGeneratorTests.cs

@ -1,25 +0,0 @@
// ==========================================================================
// ClientKeyGeneratorTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Xunit;
namespace Squidex.Write.Apps
{
public class ClientKeyGeneratorTests
{
private readonly ClientKeyGenerator sut = new ClientKeyGenerator();
[Fact]
public void Should_create_very_long_client_key()
{
var key = sut.GenerateKey();
Assert.Equal(44, key.Length);
}
}
}

34
tests/Squidex.Write.Tests/Schemas/SchemaCommandHandlerTests.cs

@ -237,6 +237,40 @@ namespace Squidex.Write.Schemas
});
}
[Fact]
public async Task AddWebhook_should_update_domain_object()
{
CreateSchema();
var context = CreateContextForCommand(new AddWebhook { Url = new Uri("http://cloud.squidex.io") });
await TestUpdate(schema, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task DeleteWebhook_should_update_domain_object()
{
var createCommand = new AddWebhook { Url = new Uri("http://cloud.squidex.io") };
CreateSchema();
CreateWebhook(createCommand);
var context = CreateContextForCommand(new DeleteWebhook { Id = createCommand.Id });
await TestUpdate(schema, async _ =>
{
await sut.HandleAsync(context);
});
}
private void CreateWebhook(AddWebhook command)
{
schema.AddWebhook(command);
}
private void CreateSchema()
{
schema.Create(CreateCommand(new CreateSchema { Name = SchemaName }));

95
tests/Squidex.Write.Tests/Schemas/SchemaDomainObjectTests.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
@ -667,6 +668,100 @@ namespace Squidex.Write.Schemas
);
}
[Fact]
public void AddWebhook_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.AddWebhook(CreateCommand(new AddWebhook { Url = new Uri("https://cloud.squidex.io") }));
});
}
[Fact]
public void AddWebhook_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.AddWebhook(CreateCommand(new AddWebhook()));
});
}
[Fact]
public void AddWebhook_should_throw_exception_if_schema_is_deleted()
{
CreateSchema();
DeleteSchema();
Assert.Throws<DomainException>(() =>
{
sut.AddWebhook(CreateCommand(new AddWebhook { Url = new Uri("https://cloud.squidex.io") }));
});
}
[Fact]
public void AddWebhook_should_update_schema_and_create_events()
{
var command = new AddWebhook { Url = new Uri("https://cloud.squidex.io") };
CreateSchema();
sut.AddWebhook(CreateCommand(command));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new WebhookAdded { Id = command.Id, Url = command.Url, SecurityToken = command.SecurityToken })
);
}
[Fact]
public void DeleteWebhook_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.DeleteWebhook(CreateCommand(new DeleteWebhook()));
});
}
[Fact]
public void DeleteWebhook_should_throw_exception_if_webhook_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.DeleteWebhook(CreateCommand(new DeleteWebhook { Id = Guid.NewGuid() }));
});
}
[Fact]
public void DeleteWebhook_should_throw_exception_if_schema_is_deleted()
{
CreateSchema();
DeleteSchema();
Assert.Throws<DomainException>(() =>
{
sut.DeleteWebhook(CreateCommand(new DeleteWebhook { Id = Guid.NewGuid() }));
});
}
[Fact]
public void DeleteWebhook_should_update_schema_and_create_events()
{
var createCommand = new AddWebhook { Url = new Uri("https://cloud.squidex.io") };
CreateSchema();
sut.AddWebhook(CreateCommand(createCommand));
sut.DeleteWebhook(CreateCommand(new DeleteWebhook { Id = createCommand.Id }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new WebhookAdded { Id = createCommand.Id, Url = createCommand.Url, SecurityToken = createCommand.SecurityToken }),
CreateEvent(new WebhookDeleted { Id = createCommand.Id })
);
}
private void CreateField()
{
sut.AddField(new AddField { Name = fieldName, Properties = new NumberFieldProperties() });

Loading…
Cancel
Save