Browse Source

Merge pull request #98 from SebastianStehle/feature-webhooks-matrix

Feature webhooks matrix
pull/103/head
Sebastian Stehle 8 years ago
committed by GitHub
parent
commit
5f98df2da3
  1. 1
      Squidex.sln.DotSettings
  2. 27
      src/Squidex.Domain.Apps.Core/Webhooks/WebhookSchema.cs
  3. 2
      src/Squidex.Domain.Apps.Events/Apps/AppContributorAssigned.cs
  4. 2
      src/Squidex.Domain.Apps.Events/Apps/AppContributorRemoved.cs
  5. 2
      src/Squidex.Domain.Apps.Events/Apps/AppCreated.cs
  6. 2
      src/Squidex.Domain.Apps.Events/Assets/AssetCreated.cs
  7. 2
      src/Squidex.Domain.Apps.Events/Assets/AssetRenamed.cs
  8. 2
      src/Squidex.Domain.Apps.Events/Assets/AssetUpdated.cs
  9. 2
      src/Squidex.Domain.Apps.Events/Contents/ContentCreated.cs
  10. 2
      src/Squidex.Domain.Apps.Events/Contents/ContentDeleted.cs
  11. 2
      src/Squidex.Domain.Apps.Events/Contents/ContentPublished.cs
  12. 2
      src/Squidex.Domain.Apps.Events/Contents/ContentUnpublished.cs
  13. 2
      src/Squidex.Domain.Apps.Events/Contents/ContentUpdated.cs
  14. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldAdded.cs
  15. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldDeleted.cs
  16. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldDisabled.cs
  17. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldEnabled.cs
  18. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldHidden.cs
  19. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldLocked.cs
  20. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldShown.cs
  21. 2
      src/Squidex.Domain.Apps.Events/Schemas/FieldUpdated.cs
  22. 3
      src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs
  23. 3
      src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs
  24. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs
  25. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaDeleted.cs
  26. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs
  27. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaPublished.cs
  28. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaUnpublished.cs
  29. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaUpdated.cs
  30. 18
      src/Squidex.Domain.Apps.Events/Webhooks/WebhookCreated.cs
  31. 17
      src/Squidex.Domain.Apps.Events/Webhooks/WebhookDeleted.cs
  32. 21
      src/Squidex.Domain.Apps.Events/Webhooks/WebhookEditEvent.cs
  33. 14
      src/Squidex.Domain.Apps.Events/Webhooks/WebhookEvent.cs
  34. 17
      src/Squidex.Domain.Apps.Events/Webhooks/WebhookUpdated.cs
  35. 10
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs
  36. 68
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaWebhookRepository_EventHandling.cs
  37. 43
      src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEntity.cs
  38. 6
      src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventEntity.cs
  39. 6
      src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventRepository.cs
  40. 49
      src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository.cs
  41. 93
      src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository_EventHandling.cs
  42. 8
      src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs
  43. 89
      src/Squidex.Domain.Apps.Read/Schemas/WebhookSender.cs
  44. 14
      src/Squidex.Domain.Apps.Read/Webhooks/IWebhookEntity.cs
  45. 2
      src/Squidex.Domain.Apps.Read/Webhooks/IWebhookEventEntity.cs
  46. 2
      src/Squidex.Domain.Apps.Read/Webhooks/Repositories/IWebhookEventRepository.cs
  47. 8
      src/Squidex.Domain.Apps.Read/Webhooks/Repositories/IWebhookRepository.cs
  48. 8
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookDequeuer.cs
  49. 42
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookEnqueuer.cs
  50. 2
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookJob.cs
  51. 2
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookJobResult.cs
  52. 2
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookResult.cs
  53. 100
      src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs
  54. 2
      src/Squidex.Domain.Apps.Write/Apps/Commands/AssignContributor.cs
  55. 2
      src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveContributor.cs
  56. 2
      src/Squidex.Domain.Apps.Write/Apps/Commands/RevokeClient.cs
  57. 2
      src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateClient.cs
  58. 2
      src/Squidex.Domain.Apps.Write/Assets/Commands/UpdateAsset.cs
  59. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs
  60. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/DeleteContent.cs
  61. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs
  62. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/PublishContent.cs
  63. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/UnpublishContent.cs
  64. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs
  65. 1
      src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs
  66. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs
  67. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs
  68. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/DeleteField.cs
  69. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/DeleteSchema.cs
  70. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/DisableField.cs
  71. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/EnableField.cs
  72. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/HideField.cs
  73. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/LockField.cs
  74. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/PublishSchema.cs
  75. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs
  76. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/ShowField.cs
  77. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/UnpublishSchema.cs
  78. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs
  79. 2
      src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs
  80. 10
      src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs
  81. 42
      src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs
  82. 23
      src/Squidex.Domain.Apps.Write/Webhooks/Commands/CreateWebhook.cs
  83. 7
      src/Squidex.Domain.Apps.Write/Webhooks/Commands/DeleteWebhook.cs
  84. 14
      src/Squidex.Domain.Apps.Write/Webhooks/Commands/UpdateWebhook.cs
  85. 25
      src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookAggregateCommand.cs
  86. 23
      src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs
  87. 77
      src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs
  88. 88
      src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs
  89. 5
      src/Squidex.Infrastructure/CQRS/Commands/DefaultDomainObjectFactory.cs
  90. 22
      src/Squidex.Infrastructure/Http/DumpFormatter.cs
  91. 1
      src/Squidex/Config/Domain/ReadModule.cs
  92. 6
      src/Squidex/Config/Domain/StoreMongoDbModule.cs
  93. 9
      src/Squidex/Config/Domain/WriteModule.cs
  94. 2
      src/Squidex/Controllers/Api/Apps/AppsController.cs
  95. 4
      src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs
  96. 7
      src/Squidex/Controllers/Api/Schemas/SchemasController.cs
  97. 9
      src/Squidex/Controllers/Api/Webhooks/Models/CreateWebhookDto.cs
  98. 29
      src/Squidex/Controllers/Api/Webhooks/Models/UpdateWebhookDto.cs
  99. 4
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookCreatedDto.cs
  100. 35
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookDto.cs

1
Squidex.sln.DotSettings

@ -20,6 +20,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AutoPropertyCanBeMadeGetOnly_002EGlobal/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AutoPropertyCanBeMadeGetOnly_002EGlobal/@EntryIndexRemoved">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverUpdated_002EGlobal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoProperty/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToAutoPropertyWhenPossible/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToConstant_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>

27
src/Squidex.Domain.Apps.Core/Webhooks/WebhookSchema.cs

@ -0,0 +1,27 @@
// ==========================================================================
// WebhookSchema.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Core.Webhooks
{
public sealed class WebhookSchema
{
public Guid SchemaId { get; set; }
public bool SendCreate { get; set; }
public bool SendUpdate { get; set; }
public bool SendDelete { get; set; }
public bool SendPublish { get; set; }
public bool SendUnpublish { get; set; }
}
}

2
src/Squidex.Domain.Apps.Events/Apps/AppContributorAssigned.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Apps
{
[TypeName("AppContributorAssignedEvent")]
public class AppContributorAssigned : AppEvent
public sealed class AppContributorAssigned : AppEvent
{
public string ContributorId { get; set; }

2
src/Squidex.Domain.Apps.Events/Apps/AppContributorRemoved.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Apps
{
[TypeName("AppContributorRemovedEvent")]
public class AppContributorRemoved : AppEvent
public sealed class AppContributorRemoved : AppEvent
{
public string ContributorId { get; set; }
}

2
src/Squidex.Domain.Apps.Events/Apps/AppCreated.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Apps
{
[TypeName("AppCreatedEvent")]
public class AppCreated : AppEvent
public sealed class AppCreated : AppEvent
{
public string Name { get; set; }
}

2
src/Squidex.Domain.Apps.Events/Assets/AssetCreated.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Assets
{
[TypeName("AssetCreatedEvent")]
public class AssetCreated : AssetEvent
public sealed class AssetCreated : AssetEvent
{
public string FileName { get; set; }

2
src/Squidex.Domain.Apps.Events/Assets/AssetRenamed.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Assets
{
[TypeName("AssetRenamedEvent")]
public class AssetRenamed : AssetEvent
public sealed class AssetRenamed : AssetEvent
{
public string FileName { get; set; }
}

2
src/Squidex.Domain.Apps.Events/Assets/AssetUpdated.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Assets
{
[TypeName("AssetUpdated")]
public class AssetUpdated : AssetEvent
public sealed class AssetUpdated : AssetEvent
{
public string MimeType { get; set; }

2
src/Squidex.Domain.Apps.Events/Contents/ContentCreated.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Contents
{
[TypeName("ContentCreatedEvent")]
public class ContentCreated : ContentEvent
public sealed class ContentCreated : ContentEvent
{
public NamedContentData Data { get; set; }
}

2
src/Squidex.Domain.Apps.Events/Contents/ContentDeleted.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Contents
{
[TypeName("ContentDeletedEvent")]
public class ContentDeleted : ContentEvent
public sealed class ContentDeleted : ContentEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Contents/ContentPublished.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Contents
{
[TypeName("ContentPublishedEvent")]
public class ContentPublished : ContentEvent
public sealed class ContentPublished : ContentEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Contents/ContentUnpublished.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Contents
{
[TypeName("ContentUnpublishedEvent")]
public class ContentUnpublished : ContentEvent
public sealed class ContentUnpublished : ContentEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Contents/ContentUpdated.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Contents
{
[TypeName("ContentUpdatedEvent")]
public class ContentUpdated : ContentEvent
public sealed class ContentUpdated : ContentEvent
{
public NamedContentData Data { get; set; }
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldAdded.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldAddedEvent")]
public class FieldAdded : FieldEvent
public sealed class FieldAdded : FieldEvent
{
public string Name { get; set; }

2
src/Squidex.Domain.Apps.Events/Schemas/FieldDeleted.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldDeletedEvent")]
public class FieldDeleted : FieldEvent
public sealed class FieldDeleted : FieldEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldDisabled.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldDisabledEvent")]
public class FieldDisabled : FieldEvent
public sealed class FieldDisabled : FieldEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldEnabled.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldEnabledEvent")]
public class FieldEnabled : FieldEvent
public sealed class FieldEnabled : FieldEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldHidden.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldHiddenEvent")]
public class FieldHidden : FieldEvent
public sealed class FieldHidden : FieldEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldLocked.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldLockedEvent")]
public class FieldLocked : FieldEvent
public sealed class FieldLocked : FieldEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldShown.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldShownEvent")]
public class FieldShown : FieldEvent
public sealed class FieldShown : FieldEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/FieldUpdated.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("FieldUpdatedEvent")]
public class FieldUpdated : FieldEvent
public sealed class FieldUpdated : FieldEvent
{
public FieldProperties Properties { get; set; }
}

3
src/Squidex.Domain.Apps.Events/Schemas/WebhookAdded.cs → src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookAdded.cs

@ -9,9 +9,10 @@
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
namespace Squidex.Domain.Apps.Events.Schemas.Old
{
[TypeName("WebhookAddedEvent")]
[Obsolete]
public sealed class WebhookAdded : SchemaEvent
{
public Guid Id { get; set; }

3
src/Squidex.Domain.Apps.Events/Schemas/WebhookDeleted.cs → src/Squidex.Domain.Apps.Events/Schemas/Old/WebhookDeleted.cs

@ -9,9 +9,10 @@
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
namespace Squidex.Domain.Apps.Events.Schemas.Old
{
[TypeName("WebhookDeletedEvent")]
[Obsolete]
public sealed class WebhookDeleted : SchemaEvent
{
public Guid Id { get; set; }

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs

@ -13,7 +13,7 @@ using SchemaFields = System.Collections.Generic.List<Squidex.Domain.Apps.Events.
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("SchemaCreatedEvent")]
public class SchemaCreated : SchemaEvent
public sealed class SchemaCreated : SchemaEvent
{
public string Name { get; set; }

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaDeleted.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("SchemaDeletedEvent")]
public class SchemaDeleted : SchemaEvent
public sealed class SchemaDeleted : SchemaEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("SchemaFieldsReorderedEvent")]
public class SchemaFieldsReordered : SchemaEvent
public sealed class SchemaFieldsReordered : SchemaEvent
{
public List<long> FieldIds { get; set; }
}

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaPublished.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("SchemaPublishedEvent")]
public class SchemaPublished : SchemaEvent
public sealed class SchemaPublished : SchemaEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaUnpublished.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("SchemaUnpublishedEvent")]
public class SchemaUnpublished : SchemaEvent
public sealed class SchemaUnpublished : SchemaEvent
{
}
}

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaUpdated.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Schemas
{
[TypeName("SchemaUpdatedEvent")]
public class SchemaUpdated : SchemaEvent
public sealed class SchemaUpdated : SchemaEvent
{
public SchemaProperties Properties { get; set; }
}

18
src/Squidex.Domain.Apps.Events/Webhooks/WebhookCreated.cs

@ -0,0 +1,18 @@
// ==========================================================================
// WebhookCreated.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Webhooks
{
[TypeName("WebhookCreatedEvent")]
public sealed class WebhookCreated : WebhookEditEvent
{
public string SharedSecret { get; set; }
}
}

17
src/Squidex.Domain.Apps.Events/Webhooks/WebhookDeleted.cs

@ -0,0 +1,17 @@
// ==========================================================================
// WebhookDeleted.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Webhooks
{
[TypeName("WebhookDeletedEventV2")]
public sealed class WebhookDeleted : WebhookEvent
{
}
}

21
src/Squidex.Domain.Apps.Events/Webhooks/WebhookEditEvent.cs

@ -0,0 +1,21 @@
// ==========================================================================
// WebhookEditEvent.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Webhooks;
namespace Squidex.Domain.Apps.Events.Webhooks
{
public abstract class WebhookEditEvent : WebhookEvent
{
public Uri Url { get; set; }
public List<WebhookSchema> Schemas { get; set; }
}
}

14
src/Squidex.Domain.Apps.Read/Schemas/ISchemaWebhookUrlEntity.cs → src/Squidex.Domain.Apps.Events/Webhooks/WebhookEvent.cs

@ -1,5 +1,5 @@
// ==========================================================================
// ISchemaWebhookUrlEntity.cs
// WebhookEvent.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -8,14 +8,10 @@
using System;
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Events.Webhooks
{
public interface ISchemaWebhookUrlEntity
public abstract class WebhookEvent : AppEvent
{
Guid Id { get; }
Uri Url { get; }
string SharedSecret { get; }
public Guid WebhookId { get; set; }
}
}
}

17
src/Squidex.Domain.Apps.Events/Webhooks/WebhookUpdated.cs

@ -0,0 +1,17 @@
// ==========================================================================
// WebhookUpdated.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Events.Webhooks
{
[TypeName("WebhookUpdatedEvent")]
public sealed class WebhookUpdated : WebhookEditEvent
{
}
}

10
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs

@ -103,16 +103,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s, registry));
}
protected Task On(WebhookAdded @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => s);
}
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => s);
}
protected Task On(SchemaDeleted @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true);

68
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaWebhookRepository_EventHandling.cs

@ -1,68 +0,0 @@
// ==========================================================================
// MongoSchemaWebhookRepository_EventHandling.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{
public partial class MongoSchemaWebhookRepository
{
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return "^schema-"; }
}
public Task On(Envelope<IEvent> @event)
{
return this.DispatchActionAsync(@event.Payload, @event.Headers);
}
protected async Task On(WebhookAdded @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
var theAppId = @event.AppId.Id;
var theSchemaId = @event.SchemaId.Id;
var webhook = SimpleMapper.Map(@event, new MongoSchemaWebhookEntity { AppId = theAppId, SchemaId = theSchemaId });
inMemoryWebhooks.GetOrAddNew(theAppId).GetOrAddNew(theSchemaId).Add(SimpleMapper.Map(@event, new ShortInfo()));
await Collection.InsertOneAsync(webhook);
}
protected async Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
inMemoryWebhooks.GetOrDefault(@event.AppId.Id)?.Remove(@event.SchemaId.Id);
await Collection.DeleteManyAsync(x => x.Id == @event.Id);
}
protected async Task On(SchemaDeleted @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
inMemoryWebhooks.GetOrDefault(@event.AppId.Id)?.Remove(@event.SchemaId.Id);
await Collection.DeleteManyAsync(x => x.SchemaId == @event.SchemaId.Id);
}
}
}

43
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaWebhookEntity.cs → src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEntity.cs

@ -1,5 +1,5 @@
// ==========================================================================
// MongoSchemaWebhookEntity.cs
// MongoWebhookEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -7,21 +7,19 @@
// ==========================================================================
using System;
using MongoDB.Bson;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Core.Webhooks;
using Squidex.Domain.Apps.Read.Webhooks;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable CollectionNeverUpdated.Global
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks
{
public class MongoSchemaWebhookEntity : ISchemaWebhookEntity
public class MongoWebhookEntity : MongoEntity, IWebhookEntity
{
[BsonId]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
[BsonRequired]
[BsonElement]
public Uri Url { get; set; }
@ -32,7 +30,15 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
[BsonRequired]
[BsonElement]
public Guid SchemaId { get; set; }
public long Version { get; set; }
[BsonRequired]
[BsonElement]
public RefToken CreatedBy { get; set; }
[BsonRequired]
[BsonElement]
public RefToken LastModifiedBy { get; set; }
[BsonRequired]
[BsonElement]
@ -53,5 +59,18 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
[BsonRequired]
[BsonElement]
public long TotalRequestTime { get; set; }
[BsonRequired]
[BsonElement]
public List<WebhookSchema> Schemas { get; set; }
[BsonRequired]
[BsonElement]
public List<Guid> SchemaIds { get; set; }
IEnumerable<WebhookSchema> IWebhookEntity.Schemas
{
get { return Schemas; }
}
}
}

6
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoWebhookEventEntity.cs → src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventEntity.cs

@ -9,11 +9,11 @@
using System;
using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Webhooks;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks
{
public sealed class MongoWebhookEventEntity : MongoEntity, IWebhookEventEntity
{
@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
[BsonRequired]
[BsonElement]
public Guid WebhookId { get; set; }
public long Version { get; set; }
[BsonRequired]
[BsonElement]

6
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoWebhookEventRepository.cs → src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookEventRepository.cs

@ -12,13 +12,13 @@ using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using NodaTime;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Webhooks;
using Squidex.Domain.Apps.Read.Webhooks.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks
{
public sealed class MongoWebhookEventRepository : MongoRepositoryBase<MongoWebhookEventEntity>, IWebhookEventRepository
{

49
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaWebhookRepository.cs → src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository.cs

@ -1,5 +1,5 @@
// ==========================================================================
// MongoSchemaWebhookRepository.cs
// MongoWebhookRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -8,38 +8,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Webhooks;
using Squidex.Domain.Apps.Read.Webhooks.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
// ReSharper disable SwitchStatementMissingSomeCases
namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks
{
public partial class MongoSchemaWebhookRepository : MongoRepositoryBase<MongoSchemaWebhookEntity>, ISchemaWebhookRepository, IEventConsumer
public partial class MongoWebhookRepository : MongoRepositoryBase<MongoWebhookEntity>, IWebhookRepository, IEventConsumer
{
private static readonly List<ShortInfo> EmptyWebhooks = new List<ShortInfo>();
private Dictionary<Guid, Dictionary<Guid, List<ShortInfo>>> inMemoryWebhooks;
private static readonly List<IWebhookEntity> EmptyWebhooks = new List<IWebhookEntity>();
private Dictionary<Guid, List<IWebhookEntity>> inMemoryWebhooks;
private readonly SemaphoreSlim lockObject = new SemaphoreSlim(1);
public sealed class ShortInfo : ISchemaWebhookUrlEntity
{
public Guid Id { get; set; }
public Uri Url { get; set; }
public string SharedSecret { get; set; }
}
public MongoSchemaWebhookRepository(IMongoDatabase database)
public MongoWebhookRepository(IMongoDatabase database)
: base(database)
{
}
@ -49,21 +38,17 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
return "Projections_SchemaWebhooks";
}
protected override Task SetupCollectionAsync(IMongoCollection<MongoSchemaWebhookEntity> collection)
protected override async Task SetupCollectionAsync(IMongoCollection<MongoWebhookEntity> collection)
{
return collection.Indexes.CreateOneAsync(Index.Ascending(x => x.SchemaId));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.SchemaIds));
}
public async Task<IReadOnlyList<ISchemaWebhookEntity>> QueryByAppAsync(Guid appId)
{
return await Collection.Find(Filter.Eq(x => x.AppId, appId)).ToListAsync();
}
public async Task<IReadOnlyList<ISchemaWebhookUrlEntity>> QueryUrlsBySchemaAsync(Guid appId, Guid schemaId)
public async Task<IReadOnlyList<IWebhookEntity>> QueryByAppAsync(Guid appId)
{
await EnsureWebooksLoadedAsync();
return inMemoryWebhooks.GetOrDefault(appId)?.GetOrDefault(schemaId)?.ToList() ?? EmptyWebhooks;
return inMemoryWebhooks.GetOrDefault(appId) ?? EmptyWebhooks;
}
public async Task TraceSentAsync(Guid webhookId, WebhookResult result, TimeSpan elapsed)
@ -103,18 +88,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
if (inMemoryWebhooks == null)
{
var result = new Dictionary<Guid, Dictionary<Guid, List<ShortInfo>>>();
inMemoryWebhooks = new Dictionary<Guid, List<IWebhookEntity>>();
var webhooks = await Collection.Find(new BsonDocument()).ToListAsync();
foreach (var webhook in webhooks)
{
var list = result.GetOrAddNew(webhook.AppId).GetOrAddNew(webhook.SchemaId);
list.Add(SimpleMapper.Map(webhook, new ShortInfo()));
inMemoryWebhooks.GetOrAddNew(webhook.AppId).Add(webhook);
}
inMemoryWebhooks = result;
}
}
finally

93
src/Squidex.Domain.Apps.Read.MongoDb/Webhooks/MongoWebhookRepository_EventHandling.cs

@ -0,0 +1,93 @@
// ==========================================================================
// MongoSchemaWebhookRepository_EventHandling.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection;
using Squidex.Domain.Apps.Events.Webhooks;
using Squidex.Domain.Apps.Read.MongoDb.Utils;
namespace Squidex.Domain.Apps.Read.MongoDb.Webhooks
{
public partial class MongoWebhookRepository
{
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return "(^webhook-)|(^schema-)"; }
}
public Task On(Envelope<IEvent> @event)
{
return this.DispatchActionAsync(@event.Payload, @event.Headers);
}
protected async Task On(WebhookCreated @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
await Collection.CreateAsync(@event, headers, w =>
{
SimpleMapper.Map(@event, w);
w.SchemaIds = w.Schemas.Select(x => x.SchemaId).ToList();
inMemoryWebhooks.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
inMemoryWebhooks.GetOrAddNew(w.AppId).Add(w);
});
}
protected async Task On(WebhookUpdated @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
await Collection.UpdateAsync(@event, headers, w =>
{
SimpleMapper.Map(@event, w);
w.SchemaIds = w.Schemas.Select(x => x.SchemaId).ToList();
inMemoryWebhooks.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
inMemoryWebhooks.GetOrAddNew(w.AppId).Add(w);
});
}
protected async Task On(SchemaDeleted @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
await Collection.UpdateAsync(@event, headers, w =>
{
w.Schemas.RemoveAll(s => s.SchemaId == @event.SchemaId.Id);
w.SchemaIds = w.Schemas.Select(x => x.SchemaId).ToList();
inMemoryWebhooks.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
inMemoryWebhooks.GetOrAddNew(w.AppId).Add(w);
});
}
protected async Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
await EnsureWebooksLoadedAsync();
inMemoryWebhooks.GetOrAddNew(@event.AppId.Id).RemoveAll(x => x.Id == @event.WebhookId);
await Collection.DeleteManyAsync(x => x.Id == @event.WebhookId);
}
}
}

8
src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs

@ -125,14 +125,6 @@ namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations
{
Remove(schemaUpdatedEvent.AppId, schemaUpdatedEvent.SchemaId);
}
else if (@event.Payload is WebhookAdded webhookAddedEvent)
{
Remove(webhookAddedEvent.AppId, webhookAddedEvent.SchemaId);
}
else if (@event.Payload is WebhookDeleted webhookDeletedEvent)
{
Remove(webhookDeletedEvent.AppId, webhookDeletedEvent.SchemaId);
}
return TaskHelper.Done;
}

89
src/Squidex.Domain.Apps.Read/Schemas/WebhookSender.cs

@ -1,89 +0,0 @@
// ==========================================================================
// WebhookSender.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Squidex.Infrastructure.Http;
// ReSharper disable SuggestVarOrType_SimpleTypes
namespace Squidex.Domain.Apps.Read.Schemas
{
public class WebhookSender
{
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
public virtual async Task<(string Dump, WebhookResult Result, TimeSpan Elapsed)> SendAsync(WebhookJob job)
{
HttpRequestMessage request = BuildRequest(job);
HttpResponseMessage response = null;
var isTimeout = false;
var watch = Stopwatch.StartNew();
try
{
using (var client = new HttpClient { Timeout = Timeout })
{
response = await client.SendAsync(request);
}
}
catch (TimeoutException)
{
isTimeout = true;
}
catch (OperationCanceledException)
{
isTimeout = true;
}
finally
{
watch.Stop();
}
var responseString = string.Empty;
if (response != null)
{
responseString = await response.Content.ReadAsStringAsync();
}
var dump = DumpFormatter.BuildDump(request, response, job.RequestBody, responseString, watch.Elapsed);
var result = WebhookResult.Failed;
if (isTimeout)
{
result = WebhookResult.Timeout;
}
else if (response?.IsSuccessStatusCode == true)
{
result = WebhookResult.Success;
}
return (dump, result, watch.Elapsed);
}
private static HttpRequestMessage BuildRequest(WebhookJob job)
{
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl)
{
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json")
};
request.Headers.Add("X-Signature", job.RequestSignature);
request.Headers.Add("User-Agent", "Squidex Webhook");
return request;
}
}
}

14
src/Squidex.Domain.Apps.Read/Schemas/ISchemaWebhookEntity.cs → src/Squidex.Domain.Apps.Read/Webhooks/IWebhookEntity.cs

@ -1,5 +1,5 @@
// ==========================================================================
// ISchemaWebhookEntity.cs
// IWebhookEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -7,12 +7,14 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Webhooks;
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public interface ISchemaWebhookEntity : ISchemaWebhookUrlEntity
public interface IWebhookEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{
Guid SchemaId { get; }
Uri Url { get; }
long TotalSucceeded { get; }
@ -21,5 +23,9 @@ namespace Squidex.Domain.Apps.Read.Schemas
long TotalTimedout { get; }
long TotalRequestTime { get; }
string SharedSecret { get; }
IEnumerable<WebhookSchema> Schemas { get; }
}
}

2
src/Squidex.Domain.Apps.Read/Schemas/IWebhookEventEntity.cs → src/Squidex.Domain.Apps.Read/Webhooks/IWebhookEventEntity.cs

@ -8,7 +8,7 @@
using NodaTime;
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public interface IWebhookEventEntity : IEntity
{

2
src/Squidex.Domain.Apps.Read/Schemas/Repositories/IWebhookEventRepository.cs → src/Squidex.Domain.Apps.Read/Webhooks/Repositories/IWebhookEventRepository.cs

@ -12,7 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using NodaTime;
namespace Squidex.Domain.Apps.Read.Schemas.Repositories
namespace Squidex.Domain.Apps.Read.Webhooks.Repositories
{
public interface IWebhookEventRepository
{

8
src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaWebhookRepository.cs → src/Squidex.Domain.Apps.Read/Webhooks/Repositories/IWebhookRepository.cs

@ -10,14 +10,12 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Schemas.Repositories
namespace Squidex.Domain.Apps.Read.Webhooks.Repositories
{
public interface ISchemaWebhookRepository
public interface IWebhookRepository
{
Task TraceSentAsync(Guid webhookId, WebhookResult result, TimeSpan elapsed);
Task<IReadOnlyList<ISchemaWebhookUrlEntity>> QueryUrlsBySchemaAsync(Guid appId, Guid schemaId);
Task<IReadOnlyList<ISchemaWebhookEntity>> QueryByAppAsync(Guid appId);
Task<IReadOnlyList<IWebhookEntity>> QueryByAppAsync(Guid appId);
}
}

8
src/Squidex.Domain.Apps.Read/Schemas/WebhookDequeuer.cs → src/Squidex.Domain.Apps.Read/Webhooks/WebhookDequeuer.cs

@ -11,7 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using NodaTime;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Webhooks.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Timers;
@ -20,14 +20,14 @@ using Squidex.Infrastructure.Timers;
// ReSharper disable MethodSupportsCancellation
// ReSharper disable InvertIf
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public sealed class WebhookDequeuer : DisposableObjectBase, IExternalSystem
{
private readonly ActionBlock<IWebhookEventEntity> requestBlock;
private readonly TransformBlock<IWebhookEventEntity, IWebhookEventEntity> blockBlock;
private readonly IWebhookEventRepository webhookEventRepository;
private readonly ISchemaWebhookRepository webhookRepository;
private readonly IWebhookRepository webhookRepository;
private readonly WebhookSender webhookSender;
private readonly CompletionTimer timer;
private readonly ISemanticLog log;
@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Read.Schemas
public WebhookDequeuer(WebhookSender webhookSender,
IWebhookEventRepository webhookEventRepository,
ISchemaWebhookRepository webhookRepository,
IWebhookRepository webhookRepository,
IClock clock,
ISemanticLog log)
{

42
src/Squidex.Domain.Apps.Read/Schemas/WebhookEnqueuer.cs → src/Squidex.Domain.Apps.Read/Webhooks/WebhookEnqueuer.cs

@ -7,25 +7,27 @@
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NodaTime;
using Squidex.Domain.Apps.Core.Webhooks;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Webhooks.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public sealed class WebhookEnqueuer : IEventConsumer
{
private const string ContentPrefix = "Content";
private static readonly Duration ExpirationTime = Duration.FromDays(2);
private readonly IWebhookEventRepository webhookEventRepository;
private readonly ISchemaWebhookRepository webhookRepository;
private readonly IWebhookRepository webhookRepository;
private readonly IClock clock;
private readonly TypeNameRegistry typeNameRegistry;
private readonly JsonSerializer webhookSerializer;
@ -42,7 +44,7 @@ namespace Squidex.Domain.Apps.Read.Schemas
public WebhookEnqueuer(TypeNameRegistry typeNameRegistry,
IWebhookEventRepository webhookEventRepository,
ISchemaWebhookRepository webhookRepository,
IWebhookRepository webhookRepository,
IClock clock,
JsonSerializer webhookSerializer)
{
@ -72,25 +74,26 @@ namespace Squidex.Domain.Apps.Read.Schemas
{
var eventType = typeNameRegistry.GetName(@event.Payload.GetType());
var webhooks = await webhookRepository.QueryUrlsBySchemaAsync(contentEvent.AppId.Id, contentEvent.SchemaId.Id);
var webhooks = await webhookRepository.QueryByAppAsync(contentEvent.AppId.Id);
if (webhooks.Count > 0)
var matchingWebhooks = webhooks.Where(w => w.Schemas.Any(s => Matchs(s, contentEvent))).ToList();
if (matchingWebhooks.Count > 0)
{
var now = clock.GetCurrentInstant();
var payload = CreatePayload(@event, eventType);
var eventPayload = CreatePayload(@event, eventType);
var eventName = $"{contentEvent.SchemaId.Name.ToPascalCase()}{CreateContentEventName(eventType)}";
foreach (var webhook in webhooks)
foreach (var webhook in matchingWebhooks)
{
await EnqueueJobAsync(payload, webhook, contentEvent, eventName, now);
await EnqueueJobAsync(eventPayload, webhook, contentEvent, eventName, now);
}
}
}
}
private async Task EnqueueJobAsync(string payload, ISchemaWebhookUrlEntity webhook, AppEvent contentEvent, string eventName, Instant now)
private async Task EnqueueJobAsync(string payload, IWebhookEntity webhook, AppEvent contentEvent, string eventName, Instant now)
{
var signature = $"{payload}{webhook.SharedSecret}".Sha256Base64();
@ -109,12 +112,23 @@ namespace Squidex.Domain.Apps.Read.Schemas
await webhookEventRepository.EnqueueAsync(job, now);
}
private static bool Matchs(WebhookSchema webhookSchema, SchemaEvent @event)
{
return
(@event.SchemaId.Id == webhookSchema.SchemaId) &&
(@event is ContentCreated && webhookSchema.SendCreate ||
@event is ContentUpdated && webhookSchema.SendUpdate ||
@event is ContentDeleted && webhookSchema.SendDelete ||
@event is ContentPublished && webhookSchema.SendPublish ||
@event is ContentUnpublished && webhookSchema.SendUnpublish);
}
private string CreatePayload(Envelope<IEvent> @event, string eventType)
{
return new JObject(
new JProperty("type", eventType),
new JProperty("payload", JObject.FromObject(@event.Payload, webhookSerializer)),
new JProperty("timestamp", @event.Headers.Timestamp().ToString()))
new JProperty("type", eventType),
new JProperty("payload", JObject.FromObject(@event.Payload, webhookSerializer)),
new JProperty("timestamp", @event.Headers.Timestamp().ToString()))
.ToString(Formatting.Indented);
}

2
src/Squidex.Domain.Apps.Read/Schemas/WebhookJob.cs → src/Squidex.Domain.Apps.Read/Webhooks/WebhookJob.cs

@ -9,7 +9,7 @@
using System;
using NodaTime;
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public sealed class WebhookJob
{

2
src/Squidex.Domain.Apps.Read/Schemas/WebhookJobResult.cs → src/Squidex.Domain.Apps.Read/Webhooks/WebhookJobResult.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public enum WebhookJobResult
{

2
src/Squidex.Domain.Apps.Read/Schemas/WebhookResult.cs → src/Squidex.Domain.Apps.Read/Webhooks/WebhookResult.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Schemas
namespace Squidex.Domain.Apps.Read.Webhooks
{
public enum WebhookResult
{

100
src/Squidex.Domain.Apps.Read/Webhooks/WebhookSender.cs

@ -0,0 +1,100 @@
// ==========================================================================
// WebhookSender.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Squidex.Infrastructure.Http;
// ReSharper disable SuggestVarOrType_SimpleTypes
namespace Squidex.Domain.Apps.Read.Webhooks
{
public class WebhookSender
{
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
public virtual async Task<(string Dump, WebhookResult Result, TimeSpan Elapsed)> SendAsync(WebhookJob job)
{
try
{
HttpRequestMessage request = BuildRequest(job);
HttpResponseMessage response = null;
var responseString = string.Empty;
var isTimeout = false;
var watch = Stopwatch.StartNew();
try
{
using (var client = new HttpClient { Timeout = Timeout })
{
response = await client.SendAsync(request);
}
}
catch (TimeoutException)
{
isTimeout = true;
}
catch (OperationCanceledException)
{
isTimeout = true;
}
catch (Exception ex)
{
responseString = ex.Message;
}
finally
{
watch.Stop();
}
if (response != null)
{
responseString = await response.Content.ReadAsStringAsync();
}
var dump = DumpFormatter.BuildDump(request, response, job.RequestBody, responseString, watch.Elapsed, isTimeout);
var result = WebhookResult.Failed;
if (isTimeout)
{
result = WebhookResult.Timeout;
}
else if (response?.IsSuccessStatusCode == true)
{
result = WebhookResult.Success;
}
return (dump, result, watch.Elapsed);
}
catch (Exception ex)
{
return (ex.Message, WebhookResult.Failed, TimeSpan.Zero);
}
}
private static HttpRequestMessage BuildRequest(WebhookJob job)
{
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl)
{
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json")
};
request.Headers.Add("X-Signature", job.RequestSignature);
request.Headers.Add("User-Agent", "Squidex Webhook");
return request;
}
}
}

2
src/Squidex.Domain.Apps.Write/Apps/Commands/AssignContributor.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public class AssignContributor : AppAggregateCommand, IValidatable
public sealed class AssignContributor : AppAggregateCommand, IValidatable
{
public string ContributorId { get; set; }

2
src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveContributor.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public class RemoveContributor : AppAggregateCommand, IValidatable
public sealed class RemoveContributor : AppAggregateCommand, IValidatable
{
public string ContributorId { get; set; }

2
src/Squidex.Domain.Apps.Write/Apps/Commands/RevokeClient.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public class RevokeClient : AppAggregateCommand, IValidatable
public sealed class RevokeClient : AppAggregateCommand, IValidatable
{
public string Id { get; set; }

2
src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateClient.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public class UpdateClient : AppAggregateCommand, IValidatable
public sealed class UpdateClient : AppAggregateCommand, IValidatable
{
public string Id { get; set; }

2
src/Squidex.Domain.Apps.Write/Assets/Commands/UpdateAsset.cs

@ -10,7 +10,7 @@ using Squidex.Infrastructure.Assets;
namespace Squidex.Domain.Apps.Write.Assets.Commands
{
public class UpdateAsset : AssetAggregateCommand
public sealed class UpdateAsset : AssetAggregateCommand
{
public AssetFile File { get; set; }

2
src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs

@ -10,7 +10,7 @@ using System;
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public class CreateContent : ContentDataCommand
public sealed class CreateContent : ContentDataCommand
{
public bool Publish { get; set; }

2
src/Squidex.Domain.Apps.Write/Contents/Commands/DeleteContent.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public class DeleteContent : ContentCommand
public sealed class DeleteContent : ContentCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public class PatchContent : ContentDataCommand
public sealed class PatchContent : ContentDataCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Contents/Commands/PublishContent.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public class PublishContent : ContentCommand
public sealed class PublishContent : ContentCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Contents/Commands/UnpublishContent.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public class UnpublishContent : ContentCommand
public sealed class UnpublishContent : ContentCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public class UpdateContent : ContentDataCommand
public sealed class UpdateContent : ContentDataCommand
{
}
}

1
src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs

@ -42,7 +42,6 @@ namespace Squidex.Domain.Apps.Write.Contents
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(schemas, nameof(schemas));
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(assetRepository, nameof(assetRepository));
Guard.NotNull(contentRepository, nameof(contentRepository));

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs

@ -13,7 +13,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class AddField : FieldCommand, IValidatable
public sealed class AddField : FieldCommand, IValidatable
{
public string Name { get; set; }

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs

@ -15,7 +15,7 @@ using SchemaFields = System.Collections.Generic.List<Squidex.Domain.Apps.Write.S
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class CreateSchema : AppCommand, IValidatable, IAggregateCommand
public sealed class CreateSchema : AppCommand, IValidatable, IAggregateCommand
{
private SchemaProperties properties;
private SchemaFields fields;

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/DeleteField.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class DeleteField : FieldCommand
public sealed class DeleteField : FieldCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/DeleteSchema.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class DeleteSchema : SchemaAggregateCommand
public sealed class DeleteSchema : SchemaAggregateCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/DisableField.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class DisableField : FieldCommand
public sealed class DisableField : FieldCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/EnableField.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class EnableField : FieldCommand
public sealed class EnableField : FieldCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/HideField.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class HideField : FieldCommand
public sealed class HideField : FieldCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/LockField.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class LockField : FieldCommand
public sealed class LockField : FieldCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/PublishSchema.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class PublishSchema : SchemaAggregateCommand
public sealed class PublishSchema : SchemaAggregateCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class ReorderFields : SchemaAggregateCommand, IValidatable
public sealed class ReorderFields : SchemaAggregateCommand, IValidatable
{
public List<long> FieldIds { get; set; }

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/ShowField.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class ShowField : FieldCommand
public sealed class ShowField : FieldCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/UnpublishSchema.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class UnpublishSchema : SchemaAggregateCommand
public sealed class UnpublishSchema : SchemaAggregateCommand
{
}
}

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class UpdateField : FieldCommand, IValidatable
public sealed class UpdateField : FieldCommand, IValidatable
{
public FieldProperties Properties { get; set; }

2
src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public class UpdateSchema : SchemaAggregateCommand, IValidatable
public sealed class UpdateSchema : SchemaAggregateCommand, IValidatable
{
public SchemaProperties Properties { get; set; }

10
src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs

@ -60,16 +60,6 @@ namespace Squidex.Domain.Apps.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.Domain.Apps.Write/Schemas/SchemaDomainObject.cs

@ -23,7 +23,6 @@ namespace Squidex.Domain.Apps.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;
@ -115,16 +114,6 @@ namespace Squidex.Domain.Apps.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;
@ -155,29 +144,6 @@ namespace Squidex.Domain.Apps.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}");
@ -335,14 +301,6 @@ namespace Squidex.Domain.Apps.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)

23
src/Squidex.Domain.Apps.Write/Webhooks/Commands/CreateWebhook.cs

@ -0,0 +1,23 @@
// ==========================================================================
// CreateWebhook.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Webhooks.Commands
{
public sealed class CreateWebhook : WebhookEditCommand
{
public string SharedSecret { get; } = RandomHash.New();
public CreateWebhook()
{
WebhookId = Guid.NewGuid();
}
}
}

7
src/Squidex.Domain.Apps.Write/Schemas/Commands/DeleteWebhook.cs → src/Squidex.Domain.Apps.Write/Webhooks/Commands/DeleteWebhook.cs

@ -6,12 +6,9 @@
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
namespace Squidex.Domain.Apps.Write.Webhooks.Commands
{
public class DeleteWebhook : SchemaAggregateCommand
public sealed class DeleteWebhook : WebhookAggregateCommand
{
public Guid Id { get; set; }
}
}

14
src/Squidex.Domain.Apps.Write/Webhooks/Commands/UpdateWebhook.cs

@ -0,0 +1,14 @@
// ==========================================================================
// UpdateWebhook.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Webhooks.Commands
{
public sealed class UpdateWebhook : WebhookEditCommand
{
}
}

25
src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookAggregateCommand.cs

@ -0,0 +1,25 @@
// ==========================================================================
// WebhookAggregateCommand.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure.CQRS.Commands;
// ReSharper disable MemberCanBeProtected.Global
namespace Squidex.Domain.Apps.Write.Webhooks.Commands
{
public abstract class WebhookAggregateCommand : AppCommand, IAggregateCommand
{
public Guid WebhookId { get; set; }
Guid IAggregateCommand.AggregateId
{
get { return WebhookId; }
}
}
}

23
src/Squidex.Domain.Apps.Write/Schemas/Commands/AddWebhook.cs → src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs

@ -1,5 +1,5 @@
// ==========================================================================
// AddWebhook.cs
// WebhookEditCommand.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -8,19 +8,30 @@
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Webhooks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
namespace Squidex.Domain.Apps.Write.Webhooks.Commands
{
public sealed class AddWebhook : SchemaAggregateCommand, IValidatable
public abstract class WebhookEditCommand : WebhookAggregateCommand, IValidatable
{
public Guid Id { get; } = Guid.NewGuid();
private List<WebhookSchema> schemas = new List<WebhookSchema>();
public Uri Url { get; set; }
public string SharedSecret { get; } = RandomHash.New();
public List<WebhookSchema> Schemas
{
get
{
return schemas ?? (schemas = new List<WebhookSchema>());
}
set
{
schemas = value;
}
}
public void Validate(IList<ValidationError> errors)
public virtual void Validate(IList<ValidationError> errors)
{
if (Url == null || !Url.IsAbsoluteUri)
{

77
src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs

@ -0,0 +1,77 @@
// ==========================================================================
// WebhookCommandMiddleware.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Write.Webhooks
{
public class WebhookCommandMiddleware : ICommandMiddleware
{
private readonly IAggregateHandler handler;
private readonly ISchemaProvider schemas;
public WebhookCommandMiddleware(IAggregateHandler handler, ISchemaProvider schemas)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(schemas, nameof(schemas));
this.handler = handler;
this.schemas = schemas;
}
protected async Task On(CreateWebhook command, CommandContext context)
{
await ValidateAsync(command, () => "Failed to create webhook");
await handler.CreateAsync<WebhookDomainObject>(context, c => c.Create(command));
}
protected async Task On(UpdateWebhook command, CommandContext context)
{
await ValidateAsync(command, () => "Failed to update content");
await handler.UpdateAsync<WebhookDomainObject>(context, c => c.Update(command));
}
protected Task On(DeleteWebhook command, CommandContext context)
{
return handler.UpdateAsync<WebhookDomainObject>(context, c => c.Delete(command));
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
if (!await this.DispatchActionAsync(context.Command, context))
{
await next();
}
}
private async Task ValidateAsync(WebhookEditCommand command, Func<string> message)
{
var results = await Task.WhenAll(
command.Schemas.Select(async schema =>
await schemas.FindSchemaByIdAsync(schema.SchemaId) == null
? new ValidationError($"Schema {schema.SchemaId} does not exist.")
: null));
var errors = results.Where(x => x != null).ToArray();
if (errors.Length > 0)
{
throw new ValidationException(message(), errors);
}
}
}
}

88
src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs

@ -0,0 +1,88 @@
// ==========================================================================
// WebhookDomainObject.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Events.Webhooks;
using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Write.Webhooks
{
public class WebhookDomainObject : DomainObjectBase
{
private bool isDeleted;
private bool isCreated;
public WebhookDomainObject(Guid id, int version)
: base(id, version)
{
}
protected void On(WebhookCreated @event)
{
isCreated = true;
}
protected void On(WebhookDeleted @event)
{
isDeleted = true;
}
public void Create(CreateWebhook command)
{
Guard.Valid(command, nameof(command), () => "Cannot create webhook");
VerifyNotCreated();
RaiseEvent(SimpleMapper.Map(command, new WebhookCreated()));
}
public void Update(UpdateWebhook command)
{
Guard.Valid(command, nameof(command), () => "Cannot update webhook");
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookUpdated()));
}
public void Delete(DeleteWebhook command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookDeleted()));
}
private void VerifyNotCreated()
{
if (isCreated)
{
throw new DomainException("Webhook has already been created.");
}
}
private void VerifyCreatedAndNotDeleted()
{
if (isDeleted || !isCreated)
{
throw new DomainException("Webhook has already been deleted or not created yet.");
}
}
protected override void DispatchEvent(Envelope<IEvent> @event)
{
this.DispatchAction(@event.Payload);
}
}
}

5
src/Squidex.Infrastructure/CQRS/Commands/DefaultDomainObjectFactory.cs

@ -27,6 +27,11 @@ namespace Squidex.Infrastructure.CQRS.Commands
{
var factoryFunction = (DomainObjectFactoryFunction<T>)serviceProvider.GetService(typeof(DomainObjectFactoryFunction<T>));
if (factoryFunction == null)
{
throw new InvalidOperationException($"No factory registered for {typeof(T)}");
}
var domainObject = factoryFunction.Invoke(id);
if (domainObject.Version != -1)

22
src/Squidex.Infrastructure/Http/DumpFormatter.cs

@ -18,7 +18,7 @@ namespace Squidex.Infrastructure.Http
{
public static class DumpFormatter
{
public static string BuildDump(HttpRequestMessage request, HttpResponseMessage response, string requestBody, string responseBody, TimeSpan elapsed)
public static string BuildDump(HttpRequestMessage request, HttpResponseMessage response, string requestBody, string responseBody, TimeSpan elapsed, bool isTimeout)
{
var writer = new StringBuilder();
@ -29,7 +29,7 @@ namespace Squidex.Infrastructure.Http
writer.AppendLine();
writer.AppendLine("Response:");
writer.AppendResponse(response, responseBody, elapsed);
writer.AppendResponse(response, responseBody, elapsed, isTimeout);
return writer.ToString();
}
@ -50,7 +50,7 @@ namespace Squidex.Infrastructure.Http
}
}
private static void AppendResponse(this StringBuilder writer, HttpResponseMessage response, string responseBody, TimeSpan elapsed)
private static void AppendResponse(this StringBuilder writer, HttpResponseMessage response, string responseBody, TimeSpan elapsed, bool isTimeout)
{
if (response != null)
{
@ -61,17 +61,21 @@ namespace Squidex.Infrastructure.Http
writer.AppendHeaders(response.Headers);
writer.AppendHeaders(response.Content?.Headers);
}
if (!string.IsNullOrWhiteSpace(responseBody))
{
writer.AppendLine();
writer.AppendLine(responseBody);
}
if (!string.IsNullOrWhiteSpace(responseBody))
{
writer.AppendLine();
writer.AppendLine(responseBody);
}
if (response != null)
{
writer.AppendLine();
writer.AppendLine($"Elapsed: {elapsed}");
}
else
if (isTimeout)
{
writer.AppendLine($"Timeout after {elapsed}");
}

1
src/Squidex/Config/Domain/ReadModule.cs

@ -21,6 +21,7 @@ using Squidex.Domain.Apps.Read.History;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Read.Schemas.Services.Implementations;
using Squidex.Domain.Apps.Read.Webhooks;
using Squidex.Domain.Users;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;

6
src/Squidex/Config/Domain/StoreMongoDbModule.cs

@ -23,8 +23,10 @@ using Squidex.Domain.Apps.Read.MongoDb.Assets;
using Squidex.Domain.Apps.Read.MongoDb.Contents;
using Squidex.Domain.Apps.Read.MongoDb.History;
using Squidex.Domain.Apps.Read.MongoDb.Schemas;
using Squidex.Domain.Apps.Read.MongoDb.Webhooks;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Domain.Apps.Read.Schemas.Services.Implementations;
using Squidex.Domain.Apps.Read.Webhooks.Repositories;
using Squidex.Domain.Users;
using Squidex.Domain.Users.MongoDb;
using Squidex.Domain.Users.MongoDb.Infrastructure;
@ -171,9 +173,9 @@ namespace Squidex.Config.Domain
.AsSelf()
.SingleInstance();
builder.RegisterType<MongoSchemaWebhookRepository>()
builder.RegisterType<MongoWebhookRepository>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<ISchemaWebhookRepository>()
.As<IWebhookRepository>()
.As<IEventConsumer>()
.As<IExternalSystem>()
.AsSelf()

9
src/Squidex/Config/Domain/WriteModule.cs

@ -13,6 +13,7 @@ using Squidex.Domain.Apps.Write.Apps;
using Squidex.Domain.Apps.Write.Assets;
using Squidex.Domain.Apps.Write.Contents;
using Squidex.Domain.Apps.Write.Schemas;
using Squidex.Domain.Apps.Write.Webhooks;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Pipeline.CommandHandlers;
@ -71,6 +72,10 @@ namespace Squidex.Config.Domain
.As<ICommandMiddleware>()
.SingleInstance();
builder.RegisterType<WebhookCommandMiddleware>()
.As<ICommandMiddleware>()
.SingleInstance();
builder.RegisterType<ETagCommandMiddleware>()
.As<ICommandMiddleware>()
.SingleInstance();
@ -87,6 +92,10 @@ namespace Squidex.Config.Domain
.AsSelf()
.SingleInstance();
builder.Register<DomainObjectFactoryFunction<WebhookDomainObject>>(c => (id => new WebhookDomainObject(id, -1)))
.AsSelf()
.SingleInstance();
builder.Register<DomainObjectFactoryFunction<SchemaDomainObject>>(c =>
{
var fieldRegistry = c.Resolve<FieldRegistry>();

2
src/Squidex/Controllers/Api/Apps/AppsController.cs

@ -96,7 +96,7 @@ namespace Squidex.Controllers.Api.Apps
var context = await CommandBus.PublishAsync(command);
var result = context.Result<EntityCreatedResult<Guid>>();
var response = new EntityCreatedDto { Id = result.ToString(), Version = result.Version };
var response = new EntityCreatedDto { Id = result.IdOrValue.ToString(), Version = result.Version };
return CreatedAtAction(nameof(GetApps), response);
}

4
src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs

@ -59,8 +59,8 @@ namespace Squidex.Controllers.Api.Schemas
var context = await CommandBus.PublishAsync(command);
var result = context.Result<EntityCreatedResult<long>>().IdOrValue;
var response = new EntityCreatedDto { Id = result.ToString() };
var result = context.Result<EntityCreatedResult<long>>();
var response = new EntityCreatedDto { Id = result.IdOrValue.ToString(), Version = result.Version };
return StatusCode(201, response);
}

7
src/Squidex/Controllers/Api/Schemas/SchemasController.cs

@ -122,9 +122,12 @@ namespace Squidex.Controllers.Api.Schemas
{
var command = request.ToCommand();
await CommandBus.PublishAsync(command);
var context = await CommandBus.PublishAsync(command);
return CreatedAtAction(nameof(GetSchema), new { name = request.Name }, new EntityCreatedDto { Id = command.Name });
var result = context.Result<EntityCreatedResult<Guid>>();
var response = new EntityCreatedDto { Id = command.Name, Version = result.Version };
return CreatedAtAction(nameof(GetSchema), new { name = request.Name }, response);
}
/// <summary>

9
src/Squidex/Controllers/Api/Webhooks/Models/CreateWebhookDto.cs

@ -7,8 +7,11 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
// ReSharper disable CollectionNeverUpdated.Global
namespace Squidex.Controllers.Api.Webhooks.Models
{
public class CreateWebhookDto
@ -18,5 +21,11 @@ namespace Squidex.Controllers.Api.Webhooks.Models
/// </summary>
[Required]
public Uri Url { get; set; }
/// <summary>
/// The schema settings.
/// </summary>
[Required]
public List<WebhookSchemaDto> Schemas { get; set; }
}
}

29
src/Squidex/Controllers/Api/Webhooks/Models/UpdateWebhookDto.cs

@ -0,0 +1,29 @@
// ==========================================================================
// UpdateWebhookDto.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Webhooks.Models
{
public class UpdateWebhookDto
{
/// <summary>
/// The url of the webhook.
/// </summary>
[Required]
public Uri Url { get; set; }
/// <summary>
/// The schema settings.
/// </summary>
[Required]
public List<WebhookSchemaDto> Schemas { get; set; }
}
}

4
src/Squidex/Controllers/Api/Webhooks/Models/WebhookCreatedDto.cs

@ -25,8 +25,8 @@ namespace Squidex.Controllers.Api.Webhooks.Models
public string SharedSecret { get; set; }
/// <summary>
/// The id of the schema.
/// The version of the schema.
/// </summary>
public string SchemaId { get; set; }
public long Version { get; set; }
}
}

35
src/Squidex/Controllers/Api/Webhooks/Models/WebhookDto.cs

@ -8,6 +8,9 @@
using System;
using System.ComponentModel.DataAnnotations;
using NodaTime;
using Squidex.Infrastructure;
using System.Collections.Generic;
namespace Squidex.Controllers.Api.Webhooks.Models
{
@ -19,9 +22,31 @@ namespace Squidex.Controllers.Api.Webhooks.Models
public Guid Id { get; set; }
/// <summary>
/// The id of the schema.
/// The user that has created the webhook.
/// </summary>
public Guid SchemaId { get; set; }
[Required]
public RefToken CreatedBy { get; set; }
/// <summary>
/// The user that has updated the webhook.
/// </summary>
[Required]
public RefToken LastModifiedBy { get; set; }
/// <summary>
/// The date and time when the webhook has been created.
/// </summary>
public Instant Created { get; set; }
/// <summary>
/// The date and time when the webhook has been modified last.
/// </summary>
public Instant LastModified { get; set; }
/// <summary>
/// The version of the webhook.
/// </summary>
public int Version { get; set; }
/// <summary>
/// The number of succceeded calls.
@ -54,5 +79,11 @@ namespace Squidex.Controllers.Api.Webhooks.Models
/// </summary>
[Required]
public string SharedSecret { get; set; }
/// <summary>
/// The schema settings.
/// </summary>
[Required]
public List<WebhookSchemaDto> Schemas { get; set; }
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save