Webhooks: fix

This commit is contained in:
pavelbannov 2022-08-18 22:06:51 +03:00
parent 753284b312
commit 585a9f8d00
13 changed files with 193 additions and 56 deletions

View File

@ -24,6 +24,8 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ASC.Core.Common.EF.Model; namespace ASC.Core.Common.EF.Model;
public class ModelBuilderWrapper public class ModelBuilderWrapper
@ -75,6 +77,11 @@ public class ModelBuilderWrapper
return this; return this;
} }
public EntityTypeBuilder<T> Entity<T>() where T : class
{
return ModelBuilder.Entity<T>();
}
public void AddDbFunction() public void AddDbFunction()
{ {
ModelBuilder ModelBuilder

View File

@ -147,16 +147,13 @@ public class DbWorker
.AsAsyncEnumerable(); .AsAsyncEnumerable();
} }
public async Task<WebhookEntry> ReadJournal(int id) public async Task<WebhooksLog> ReadJournal(int id)
{ {
using var webhooksDbContext = _dbContextFactory.CreateDbContext(); using var webhooksDbContext = _dbContextFactory.CreateDbContext();
return await webhooksDbContext.WebhooksLogs return await webhooksDbContext.WebhooksLogs
.AsNoTracking() .AsNoTracking()
.Where(it => it.Id == id) .Where(it => it.Id == id)
.Join(webhooksDbContext.WebhooksConfigs, t => t.ConfigId, t => t.Id, (payload, config) => new { payload, config })
.Select(t => new WebhookEntry { Id = t.payload.Id, Name = t.config.Name, Payload = t.payload.RequestPayload, SecretKey = t.config.SecretKey, Uri = t.config.Uri })
.OrderBy(t => t.Id)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
} }

View File

@ -1,36 +0,0 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Webhooks.Core.EF.Model;
public class WebhookEntry
{
public int Id { get; set; }
public string Name { get; set; }
public string Payload { get; set; }
public string SecretKey { get; set; }
public string Uri { get; set; }
}

View File

@ -55,7 +55,10 @@ public static class WebhooksConfigExtension
modelBuilder.Entity<WebhooksConfig>(entity => modelBuilder.Entity<WebhooksConfig>(entity =>
{ {
entity.HasKey(e => new { e.Id }) entity.HasKey(e => new { e.Id })
.HasName("PRIMARY"); .HasName("PRIMARY");
entity.HasIndex(e => e.TenantId)
.HasDatabaseName("tenant_id");
entity.ToTable("webhooks_config") entity.ToTable("webhooks_config")
.HasCharSet("utf8"); .HasCharSet("utf8");
@ -97,7 +100,10 @@ public static class WebhooksConfigExtension
entity.HasKey(e => new { e.Id }) entity.HasKey(e => new { e.Id })
.HasName("PRIMARY"); .HasName("PRIMARY");
entity.ToTable("webhooks_config"); entity.ToTable("webhooks_config");
entity.HasIndex(e => e.TenantId)
.HasDatabaseName("tenant_id");
entity.Property(e => e.Id) entity.Property(e => e.Id)
.HasColumnType("int") .HasColumnType("int")

View File

@ -40,13 +40,17 @@ public class WebhooksLog
public int Status { get; set; } public int Status { get; set; }
public int TenantId { get; set; } public int TenantId { get; set; }
public Guid Uid { get; set; } public Guid Uid { get; set; }
public DateTime? Delivery { get; set; } public DateTime? Delivery { get; set; }
public WebhooksConfig Config { get; set; }
} }
public static class WebhooksPayloadExtension public static class WebhooksPayloadExtension
{ {
public static ModelBuilderWrapper AddWebhooksLog(this ModelBuilderWrapper modelBuilder) public static ModelBuilderWrapper AddWebhooksLog(this ModelBuilderWrapper modelBuilder)
{ {
modelBuilder.Entity<WebhooksLog>().Navigation(e => e.Config).AutoInclude();
modelBuilder modelBuilder
.Add(MySqlAddWebhooksLog, Provider.MySql) .Add(MySqlAddWebhooksLog, Provider.MySql)
.Add(PgSqlAddWebhooksLog, Provider.PostgreSql); .Add(PgSqlAddWebhooksLog, Provider.PostgreSql);
@ -62,7 +66,10 @@ public static class WebhooksPayloadExtension
.HasName("PRIMARY"); .HasName("PRIMARY");
entity.ToTable("webhooks_logs") entity.ToTable("webhooks_logs")
.HasCharSet("utf8"); .HasCharSet("utf8");
entity.HasIndex(e => e.TenantId)
.HasDatabaseName("tenant_id");
entity.Property(e => e.Id) entity.Property(e => e.Id)
.HasColumnType("int") .HasColumnType("int")
@ -135,7 +142,10 @@ public static class WebhooksPayloadExtension
entity.HasKey(e => new { e.Id }) entity.HasKey(e => new { e.Id })
.HasName("PRIMARY"); .HasName("PRIMARY");
entity.ToTable("webhooks_logs"); entity.ToTable("webhooks_logs");
entity.HasIndex(e => e.TenantId)
.HasDatabaseName("tenant_id");
entity.Property(e => e.Id) entity.Property(e => e.Id)
.HasColumnType("int") .HasColumnType("int")

View File

@ -53,8 +53,7 @@ public class WebhookSender
using var scope = _scopeFactory.CreateScope(); using var scope = _scopeFactory.CreateScope();
var dbWorker = scope.ServiceProvider.GetRequiredService<DbWorker>(); var dbWorker = scope.ServiceProvider.GetRequiredService<DbWorker>();
var entry = await dbWorker.ReadJournal(webhookRequest.Id); var entry = await dbWorker.ReadJournal(webhookRequest.Id);
var data = entry.Payload;
var status = 0; var status = 0;
string responsePayload = null; string responsePayload = null;
@ -65,13 +64,13 @@ public class WebhookSender
try try
{ {
var httpClient = _clientFactory.CreateClient("webhook"); var httpClient = _clientFactory.CreateClient("webhook");
var request = new HttpRequestMessage(HttpMethod.Post, entry.Uri) var request = new HttpRequestMessage(HttpMethod.Post, entry.Config.Uri)
{ {
Content = new StringContent(data, Encoding.UTF8, "application/json") Content = new StringContent(entry.RequestPayload, Encoding.UTF8, "application/json")
}; };
request.Headers.Add("Accept", "*/*"); request.Headers.Add("Accept", "*/*");
request.Headers.Add("Secret", "SHA256=" + GetSecretHash(entry.SecretKey, data)); request.Headers.Add("Secret", "SHA256=" + GetSecretHash(entry.Config.SecretKey, entry.RequestPayload));
requestHeaders = JsonSerializer.Serialize(request.Headers.ToDictionary(r => r.Key, v => v.Value), _jsonSerializerOptions); requestHeaders = JsonSerializer.Serialize(request.Headers.ToDictionary(r => r.Key, v => v.Value), _jsonSerializerOptions);
var response = await httpClient.SendAsync(request, cancellationToken); var response = await httpClient.SendAsync(request, cancellationToken);

View File

@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace ASC.Migrations.MySql.Migrations.WebhooksDb namespace ASC.Migrations.MySql.Migrations.WebhooksDb
{ {
[DbContext(typeof(WebhooksDbContext))] [DbContext(typeof(WebhooksDbContext))]
[Migration("20220818131736_WebhooksDbContext_Upgrade1")] [Migration("20220818144209_WebhooksDbContext_Upgrade1")]
partial class WebhooksDbContext_Upgrade1 partial class WebhooksDbContext_Upgrade1
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -61,6 +61,9 @@ namespace ASC.Migrations.MySql.Migrations.WebhooksDb
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_config", (string)null); b.ToTable("webhooks_config", (string)null);
b.HasAnnotation("MySql:CharSet", "utf8"); b.HasAnnotation("MySql:CharSet", "utf8");
@ -134,10 +137,26 @@ namespace ASC.Migrations.MySql.Migrations.WebhooksDb
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("ConfigId");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_logs", (string)null); b.ToTable("webhooks_logs", (string)null);
b.HasAnnotation("MySql:CharSet", "utf8"); b.HasAnnotation("MySql:CharSet", "utf8");
}); });
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
{
b.HasOne("ASC.Webhooks.Core.EF.Model.WebhooksConfig", "Config")
.WithMany()
.HasForeignKey("ConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Config");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -65,10 +65,49 @@ namespace ASC.Migrations.MySql.Migrations.WebhooksDb
nullable: false, nullable: false,
defaultValue: "") defaultValue: "")
.Annotation("MySql:CharSet", "utf8"); .Annotation("MySql:CharSet", "utf8");
migrationBuilder.CreateIndex(
name: "IX_webhooks_logs_config_id",
table: "webhooks_logs",
column: "config_id");
migrationBuilder.CreateIndex(
name: "tenant_id",
table: "webhooks_logs",
column: "tenant_id");
migrationBuilder.CreateIndex(
name: "tenant_id",
table: "webhooks_config",
column: "tenant_id");
migrationBuilder.AddForeignKey(
name: "FK_webhooks_logs_webhooks_config_config_id",
table: "webhooks_logs",
column: "config_id",
principalTable: "webhooks_config",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
} }
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropForeignKey(
name: "FK_webhooks_logs_webhooks_config_config_id",
table: "webhooks_logs");
migrationBuilder.DropIndex(
name: "IX_webhooks_logs_config_id",
table: "webhooks_logs");
migrationBuilder.DropIndex(
name: "tenant_id",
table: "webhooks_logs");
migrationBuilder.DropIndex(
name: "tenant_id",
table: "webhooks_config");
migrationBuilder.DropColumn( migrationBuilder.DropColumn(
name: "delivery", name: "delivery",
table: "webhooks_logs"); table: "webhooks_logs");

View File

@ -59,6 +59,9 @@ namespace ASC.Migrations.MySql.Migrations
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_config", (string)null); b.ToTable("webhooks_config", (string)null);
b.HasAnnotation("MySql:CharSet", "utf8"); b.HasAnnotation("MySql:CharSet", "utf8");
@ -132,10 +135,26 @@ namespace ASC.Migrations.MySql.Migrations
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("ConfigId");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_logs", (string)null); b.ToTable("webhooks_logs", (string)null);
b.HasAnnotation("MySql:CharSet", "utf8"); b.HasAnnotation("MySql:CharSet", "utf8");
}); });
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
{
b.HasOne("ASC.Webhooks.Core.EF.Model.WebhooksConfig", "Config")
.WithMany()
.HasForeignKey("ConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Config");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb
{ {
[DbContext(typeof(WebhooksDbContext))] [DbContext(typeof(WebhooksDbContext))]
[Migration("20220818131736_WebhooksDbContext_Upgrade1")] [Migration("20220818144209_WebhooksDbContext_Upgrade1")]
partial class WebhooksDbContext_Upgrade1 partial class WebhooksDbContext_Upgrade1
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -64,6 +64,9 @@ namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_config", (string)null); b.ToTable("webhooks_config", (string)null);
}); });
@ -131,8 +134,24 @@ namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("ConfigId");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_logs", (string)null); b.ToTable("webhooks_logs", (string)null);
}); });
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
{
b.HasOne("ASC.Webhooks.Core.EF.Model.WebhooksConfig", "Config")
.WithMany()
.HasForeignKey("ConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Config");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -55,10 +55,49 @@ namespace ASC.Migrations.PostgreSql.Migrations.WebhooksDb
maxLength: 50, maxLength: 50,
nullable: false, nullable: false,
defaultValue: ""); defaultValue: "");
migrationBuilder.CreateIndex(
name: "IX_webhooks_logs_config_id",
table: "webhooks_logs",
column: "config_id");
migrationBuilder.CreateIndex(
name: "tenant_id",
table: "webhooks_logs",
column: "tenant_id");
migrationBuilder.CreateIndex(
name: "tenant_id",
table: "webhooks_config",
column: "tenant_id");
migrationBuilder.AddForeignKey(
name: "FK_webhooks_logs_webhooks_config_config_id",
table: "webhooks_logs",
column: "config_id",
principalTable: "webhooks_config",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
} }
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropForeignKey(
name: "FK_webhooks_logs_webhooks_config_config_id",
table: "webhooks_logs");
migrationBuilder.DropIndex(
name: "IX_webhooks_logs_config_id",
table: "webhooks_logs");
migrationBuilder.DropIndex(
name: "tenant_id",
table: "webhooks_logs");
migrationBuilder.DropIndex(
name: "tenant_id",
table: "webhooks_config");
migrationBuilder.DropColumn( migrationBuilder.DropColumn(
name: "delivery", name: "delivery",
table: "webhooks_logs"); table: "webhooks_logs");

View File

@ -62,6 +62,9 @@ namespace ASC.Migrations.PostgreSql.Migrations
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_config", (string)null); b.ToTable("webhooks_config", (string)null);
}); });
@ -129,8 +132,24 @@ namespace ASC.Migrations.PostgreSql.Migrations
b.HasKey("Id") b.HasKey("Id")
.HasName("PRIMARY"); .HasName("PRIMARY");
b.HasIndex("ConfigId");
b.HasIndex("TenantId")
.HasDatabaseName("tenant_id");
b.ToTable("webhooks_logs", (string)null); b.ToTable("webhooks_logs", (string)null);
}); });
modelBuilder.Entity("ASC.Webhooks.Core.EF.Model.WebhooksLog", b =>
{
b.HasOne("ASC.Webhooks.Core.EF.Model.WebhooksConfig", "Config")
.WithMany()
.HasForeignKey("ConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Config");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -28,9 +28,9 @@ namespace ASC.Web.Api.ApiModels.ResponseDto;
public class WebhooksLogDto : IMapFrom<WebhooksLog> public class WebhooksLogDto : IMapFrom<WebhooksLog>
{ {
public string ConfigId { get; set; }
public DateTime CreationTime { get; set; }
public int Id { get; set; } public int Id { get; set; }
public string ConfigName { get; set; }
public DateTime CreationTime { get; set; }
public string Method { get; set; } public string Method { get; set; }
public string Route { get; set; } public string Route { get; set; }
public string RequestHeaders { get; set; } public string RequestHeaders { get; set; }