Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"Kontent.Ai.ModelGenerator": {
"version": "8.0.0",
"version": "9.0.0",
"commands": [
"KontentModelGenerator"
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Kontent.Ai.Delivery.Abstractions;
Expand All @@ -10,55 +9,38 @@
namespace Kontent.Ai.Boilerplate.Areas.WebHooks.Controllers
{
[Area("WebHooks")]
public class WebhooksController : Controller
public class WebhooksController(IDeliveryCacheManager cacheManager) : Controller
{
private readonly IDeliveryCacheManager _cacheManager;

public WebhooksController(IDeliveryCacheManager cacheManager)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep the original constructor, rather then using this hybrid solution for field validation. but it is up to your decision.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you might be right, especially since rest of the project doesn't use primary constructors either. reverting.

_cacheManager = cacheManager ?? throw new ArgumentNullException(nameof(cacheManager));
}
private readonly IDeliveryCacheManager _cacheManager = cacheManager ?? throw new ArgumentNullException(nameof(cacheManager));

[HttpPost]
public async Task<IActionResult> Index([FromBody] DeliveryWebhookModel model)
public async Task<IActionResult> Index([FromBody] WebhookNotification? webhook)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we support both until the 1.11.? or is it ok to cause this breaking now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the underlying package is https://github.com/kontent-ai/aspnetcore-extensions and it still supports legacy webhooks as well. the boilerplate should ideally use the most up-to-date approach and since we'll be removing legacy webhooks soon, I don't see a problem with adopting the new webhooks (wouldn't want users to scaffold a new project with webhooks that won't be a thing in a month).

{
if (model != null)
if (webhook is null)
{
var dependencies = new HashSet<string>();
if (model.Data.Items?.Any() == true)
{
foreach (var item in model.Data.Items ?? Enumerable.Empty<DeliveryWebhookItem>())
{
dependencies.Add(CacheHelpers.GetItemDependencyKey(item.Codename));
}

dependencies.Add(CacheHelpers.GetItemsDependencyKey());
Copy link
Contributor

@sevcik-martin sevcik-martin Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about items dependencyKey?
also when there is taxonomy update, there were other dependecies as well.
Current webhooks do not include info about touched dependencies though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this was way too simple. I added more complex logic with pattern matching to wipe the keys of (potentially) dependent entities

}

if (model.Data.Taxonomies?.Any() == true)
{
foreach (var taxonomy in model.Data.Taxonomies ?? Enumerable.Empty<Taxonomy>())
{
dependencies.Add(CacheHelpers.GetTaxonomyDependencyKey(taxonomy.Codename));
}

dependencies.Add(CacheHelpers.GetTaxonomiesDependencyKey());
dependencies.Add(CacheHelpers.GetItemsDependencyKey());
dependencies.Add(CacheHelpers.GetTypesDependencyKey());
}
return Ok();
}

if (model.Message.Type == "content_type")
{
dependencies.Add(CacheHelpers.GetTypesDependencyKey());
}
var tasks = (webhook.Notifications ?? Enumerable.Empty<WebhookModel>())
.Select(GetDependencyKey)
.Where(key => key is not null)
.Distinct(StringComparer.Ordinal)
.Select(key => _cacheManager.InvalidateDependencyAsync(key!));

foreach (var dependency in dependencies)
{
await _cacheManager.InvalidateDependencyAsync(dependency);
}
}
await Task.WhenAll(tasks);

return Ok();
}

private static string? GetDependencyKey(WebhookModel notification) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this same for every webhook model?

notification switch
{
{ Message.ObjectType: "content_item", Data.System.Codename: var code } => CacheHelpers.GetItemDependencyKey(code),
{ Message.ObjectType: "taxonomy", Data.System.Codename: var code } => CacheHelpers.GetTaxonomyDependencyKey(code),
{ Message.ObjectType: "language" } => CacheHelpers.GetLanguagesDependencyKey(),
{ Message.ObjectType: "content_type" } => CacheHelpers.GetTypesDependencyKey(),
// there's no cache helper in the SDK for asset notifications yet
_ => null
};
}
}
11 changes: 6 additions & 5 deletions src/content/Kontent.Ai.Boilerplate/Kontent.Ai.Boilerplate.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<UserSecretsId>Kontent.Ai.Boilerplate</UserSecretsId>
<PackageId>Kontent.Ai.Boilerplate</PackageId>
<Authors>Kontent s.r.o.</Authors>
Expand All @@ -22,17 +22,18 @@
<PackageTags>kontent-ai;mvc;aspnet;aspnetmvc;dotnetcore;dotnet;aspnetcore</PackageTags>
<NuspecFile>$(MSBuildThisFileDirectory)..\..\Template.nuspec</NuspecFile>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<Content Include="IISUrlRewrite.xml" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Kontent.Ai.AspNetCore" Version="0.13.1" />
<PackageReference Include="Kontent.Ai.Delivery" Version="17.0.0" />
<PackageReference Include="Kontent.Ai.Delivery.Caching" Version="17.0.0" />
<PackageReference Include="SimpleMvcSitemap" Version="4.0.0" />
<PackageReference Include="Kontent.Ai.AspNetCore" Version="0.14.1" />
<PackageReference Include="Kontent.Ai.Delivery" Version="18.3.0" />
<PackageReference Include="Kontent.Ai.Delivery.Caching" Version="18.3.0" />
<PackageReference Include="SimpleMvcSitemap" Version="4.0.1" />
<None Include="../../../README.md" Pack="true" PackagePath=""/>
<None Include="../../../img/kai-logo-symbol-color-rgb.png" Pack="true" PackagePath=""/>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Kontent.Ai.AspNetCore.Webhooks.Models;
using Kontent.Ai.Boilerplate.Areas.WebHooks.Controllers;
Expand All @@ -8,51 +9,48 @@

namespace Kontent.Ai.Boilerplate.Tests.Areas.WebHooks.Controllers
{
public enum PayloadType
{
Items,
Taxonomies
}

public class WebhookControllerTests
{
[Theory]
[InlineData(PayloadType.Items, "content_item", "article", "upsert")]
[InlineData(PayloadType.Items, "content_item_variant", "article", "upsert")]
[InlineData(PayloadType.Items, "content_type", "article", "upsert")]
[InlineData(PayloadType.Taxonomies, "taxonomy", "personas", "upsert")]
[InlineData(PayloadType.Items, "lorem_ipsum", "dolor_sit_amet", "upsert")]
[InlineData(PayloadType.Taxonomies, "lorem_ipsum", "dolor_sit_amet", "upsert")]
[InlineData(PayloadType.Items, "content_item", "article", "lorem_ipsum")]
[InlineData(PayloadType.Items, "content_item_variant", "article", "lorem_ipsum")]
[InlineData(PayloadType.Items, "content_type", "article", "lorem_ipsum")]
[InlineData(PayloadType.Taxonomies, "taxonomy", "personas", "lorem_ipsum")]
[InlineData(PayloadType.Items, "lorem_ipsum", "dolor_sit_amet", "lorem_ipsum")]
[InlineData(PayloadType.Taxonomies, "lorem_ipsum", "dolor_sit_amet", "lorem_ipsum")]
public async Task ReturnsOkWheneverPossible(PayloadType payloadType, string artefactType, string dataType, string operation)
[InlineData("content_item", "article", "upsert")]
[InlineData("content_item_variant", "article", "upsert")]
[InlineData("content_type", "article", "upsert")]
[InlineData("taxonomy", "personas", "upsert")]
[InlineData("lorem_ipsum", "dolor_sit_amet", "upsert")]
[InlineData("content_item", "article", "lorem_ipsum")]
[InlineData("content_item_variant", "article", "lorem_ipsum")]
[InlineData("content_type", "article", "lorem_ipsum")]
[InlineData("taxonomy", "personas", "lorem_ipsum")]
[InlineData("lorem_ipsum", "dolor_sit_amet", "lorem_ipsum")]
public async Task ReturnsOkWheneverPossible(string objectType, string codename, string action)
{
DeliveryWebhookItem[] items = null;
Taxonomy[] taxonomies = null;

switch (payloadType)
{
case PayloadType.Items:
items = new[] { new DeliveryWebhookItem { Codename = "Test", Type = dataType } };
break;
case PayloadType.Taxonomies:
taxonomies = new[] { new Taxonomy { Codename = "Test" } };
break;
}

var model = new DeliveryWebhookModel
var webhook = new WebhookNotification
{
Data = new DeliveryWebhookData { Items = items, Taxonomies = taxonomies },
Message = new Message { Type = artefactType, Operation = operation }
Notifications =
[
new WebhookModel
{
Data = new WebhookData
{
System = new WebhookItem
{
Codename = codename
}
},
Message = new WebhookMessage
{
ObjectType = objectType,
Action = action,
DeliverySlot = "published",
EnvironmentId = Guid.NewGuid()
}
}
]
};

var cacheManger = A.Fake<IDeliveryCacheManager>();
var controller = new WebhooksController(cacheManger);
var result = (StatusCodeResult)await controller.Index(model);
var result = (StatusCodeResult)await controller.Index(webhook);

Assert.InRange(result.StatusCode, 200, 299);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FakeItEasy" Version="6.2.1" />
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="FakeItEasy" Version="8.3.0" />
<PackageReference Include="FluentAssertions" Version="8.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="RichardSzalay.MockHttp" Version="7.0.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.3">
<PackageReference Include="coverlet.collector" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="3.0.3">
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down