Code Monkey home page Code Monkey logo

aspnetcore-extensions's Introduction

ASP.NET Core extensions for Kontent.ai apps

NuGet Downloads Build & Test codecov Stack Overflow Discord

Tag Helpers

img-asset tag helper

Useful for rendering responsive images. Supports Assets and Inline images in rich-text elements.

appsettings.json:

...
"ImageTransformationOptions": {
    "ResponsiveWidths": [ 200, 300, 400, 600, 800, 1000, 1200, 1400, 1600, 2000 ]
  }
...

\_ViewImports.cshtml:

@addTagHelper *, Kontent.Ai.AspNetCore

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // Adds services required for using options.
    services.AddOptions();

    // Register the ImageTransformationOptions required by Kontent.ai tag helpers
    services.Configure<ImageTransformationOptions>(Configuration.GetSection(nameof(ImageTransformationOptions)));
}

View.cshtml:

<img-asset asset="@Model.TeaserImage.First()" class="img-responsive" default-width="300">
  <media-condition min-width="769" image-width="300" />
  <media-condition min-width="330" max-width="768" image-width="689" />
</img-asset>

Output

<img
  class="img-responsive"
  alt="Coffee Brewing Techniques"
  sizes="(min-width: 769px) 300px, (max-width: 768px) and (min-width: 330px) 689px, 300px"
  src="https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=2000"
  srcset="
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=200   200w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=300   300w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=400   400w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=600   600w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=800   800w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=1000 1000w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=1200 1200w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=1400 1400w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=1600 1600w,
    https://assets-us-01.kc-usercontent.com/975bf280-fd91-488c-994c-2f04416e5ee3/fcbb12e6-66a3-4672-85d9-d502d16b8d9c/which-brewing-fits-you-1080px.jpg?w=2000 2000w
  "
  title="Coffee Brewing Techniques"
/>

Webhooks

Package provides a model for webhook deserialization: WebhookNotification. Legacy webhooks are supported via classes DeliveryWebhookModel and ManagementWebhookModel, to be used with legacy (preview) delivery API and management API triggers respectively. See Webhooks reference in Kontent.ai documentation.

Middlewares

Webhook signature verification middleware

This middleware verifies the X-Kontent-ai-Signature (and the legacy X-KC-Signature) header. Returns 401 response if the signature is invalid.

appsettings.json:

...
"WebhookOptions": {
    "Secret": "<your_secret>"
  },
...

Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  // Register the validation middleware for any number of controllers that serve for processing webhooks
  app.UseWebhookSignatureValidator(context => context.Request.Path.StartsWithSegments("/webhooks/webhooks", StringComparison.OrdinalIgnoreCase), Configuration.GetSection(nameof(WebhookOptions)));
}

aspnetcore-extensions's People

Contributors

eduard-actum avatar kontent-ai-bot avatar petrsvihlik avatar pokornyd avatar simply007 avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aspnetcore-extensions's Issues

Cannot deserialize WebhookModel

Brief bug description

Deserializing webhook payloads via WebhookModel does not work with various payloads. As an example, the following payload as the result of a publish event is fine:

{
    "data": {
        "items": [
            {
                "id": "46763b84-ac6d-4347-b18a-41f8ece0badd",
                "codename": "test_publish_published_webhook",
                "language": "default",
                "type": "publish_test",
                "collection": "default"
            }
        ],
        "taxonomies": []
    },
    "message": {
        "id": "ce7bd31a-411e-4a54-af1c-7fa46bb7c1f5",
        "project_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
        "type": "content_item_variant",
        "operation": "publish",
        "api_name": "delivery_production",
        "created_timestamp": "2022-04-12T16:02:27.0593617Z",
        "webhook_url": "https://routing.azurewebsites.net/api/KontentWebhook"
    }
}

whereas the payload for a workflow event change does not:

{
    "data": {
        "items": [
            {
                "item": {
                    "id": "66d00376-ee1d-4ed1-9a3c-a88110e65190"
                },
                "language": {
                    "id": "00000000-0000-0000-0000-000000000000"
                },
                "transition_from": {
                    "id": "fe59bae0-ead6-4145-9ea8-acb398218338"
                },
                "transition_to": {
                    "id": "02907d8d-2639-432a-9ea7-633f9e3e050e"
                }
            }
        ]
    },
    "message": {
        "id": "a94ff0a8-39cb-4975-9b65-ba7ba7305693",
        "project_id": "513fdcd0-795a-01d5-a3d4-97bb0ad9c331",
        "type": "content_item_variant",
        "operation": "change_workflow_step",
        "api_name": "content_management",
        "created_timestamp": "2022-04-12T13:39:34.4449999Z",
        "webhook_url": "https://45d3-84-67-20-182.ngrok.io/webhooks/webhooks"
    }
}

There are two issues with the above:

  1. language is defined as "language":{"id": "00000000-0000-0000-0000-000000000000"}, but in the c# class is a string and causes a JSON exception when deserializing: The JSON value could not be converted to System.String. Path: $.data.items[0].language | LineNumber: 0 | BytePositionInLine: 84.
  2. transition_from and transition_to are not covered in the class. (This for me is less of a problem as they are ignored)

Also of interest are that the language element in both of the above messages are different; the publish event uses a codename, whereas the workflow change uses a reference identifier.

Repro steps

Replicated in LINQPad 7, using .NET 6, System.Text.Json, and Kentico.Kontent.AspNetCore 0.12.0-beta3

void Main()
{
	"publish".Dump();
	var model = System.Text.Json.JsonSerializer.Deserialize<WebhookModel>(publishJson);

	model.Dump();

	model = System.Text.Json.JsonSerializer.Deserialize<WebhookModel>(workflowJson);

	model.Dump();
}

// You can define other methods, fields, classes and namespaces here
string publishJson = "{\"data\":{\"items\":[{\"id\":\"46763b84-ac6d-4347-b18a-41f8ece0badd\",\"codename\":\"test_publish_published_webhook\",\"language\":\"default\",\"type\":\"publish_test\",\"collection\":\"default\"}],\"taxonomies\":[]},\"message\":{\"id\":\"ce7bd31a-411e-4a54-af1c-7fa46bb7c1f5\",\"project_id\":\"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa\",\"type\":\"content_item_variant\",\"operation\":\"publish\",\"api_name\":\"delivery_production\",\"created_timestamp\":\"2022-04-12T16:02:27.0593617Z\",\"webhook_url\":\"https://routing.azurewebsites.net/api/KontentWebhook\"}}";
string workflowJson = "{\"data\":{\"items\":[{\"item\":{\"id\":\"66d00376-ee1d-4ed1-9a3c-a88110e65190\"},\"language\":{\"id\":\"00000000-0000-0000-0000-000000000000\"}}]},\"message\":{\"id\":\"a94ff0a8-39cb-4975-9b65-ba7ba7305693\",\"project_id\":\"aaaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa\",\"type\":\"content_item_variant\",\"operation\":\"change_workflow_step\",\"api_name\":\"content_management\",\"created_timestamp\":\"2022-04-12T13:39:34.4449999Z\",\"webhook_url\":\"https://45d3-84-67-20-182.ngrok.io/webhooks/webhooks\"}}";

Expected behavior

Model should be consistent and should deserialize correctly

Test environment

  • .NET 6, macos and Windows (arm64)
  • Kentico.Kontent.AspNetCore 0.12.0-beta3

Screenshots

Add links to screenshots, if possible.

Switch the CI to GitHub Actions

Motivation

  • Create a PoC of automatic building, testing, and deploying using GH Actions to verify the possibility of migrating from AppVeyor.

Acceptance Criteria

  • The codebase is built and tested with every commit and PR (=> we know the status of the codebase)
  • Failing tests will make the build fail and prevent any further steps (deployment)
  • Artifacts are collected for every build Can be, it's easy but not necessary. We'll collect artifacts only for Releases.
  • It's possible to create a "Release" from the master branch
    • The release will trigger a workflow that'll build the master branch and deploy the resulting NuGet package to NuGet.org
    • Symbol packages are also deployed (snupkg)
    • The NuGet package is correctly versioned (semver)
    • The version of the NuGet package can be defined as the tag version of the release
    • Allow pre-release suffixes
    • Upload artifacts to GH release, or this
    • Update release instructions in affected repos
    • Set status checks for protected branches
    • Exclude test projects from coverage

Optionally

  • Define environments and require approval to proceed with deployment (when the build and tests pass ok) Gated builds work well.
  • Try to replace Directory.Build.props with -p:NuspecProperties="Version=${{ steps.get_version.outputs.version-without-v }}
  • Try reporting without /p:CoverletOutputFormat=opencover doesn't work
  • Deploy pre-releases to a different feed/unlisted? (eg. based on the event type prereleased) There is no switch in the NuGet CLI to publish a package as unlisted right now. It'd be an option to publish the package to MyGet for instance. Not going to explore this any further ATM.
  • Add code coverage using coverlet collector + codecov + msdocs
  • Create a reusable composite action Impossible due to actions/runner#646 (there is a workaround though)

Proposed solution

Versioning

  • Employ Directory.build.props for defining the version or version patching (AssemblyInfo, *.csprojs)

Examples from AppVeyor:
<Project><PropertyGroup><Version>$($ENV:APPVEYOR_BUILD_VERSION)</Version></PropertyGroup></Project>" | out-file "Directory.build.props (before build)
And:

dotnet_csproj:
  patch: true
  file: '**\*.csproj'
  version: '{version}'
  version_prefix: '{version}'
  package_version: '{version}'
  assembly_version: '{version}'
  file_version: '{version}'
  informational_version: '{version}'

Coverage

$buildConfig = if ($ENV:CONFIGURATION -eq $null) { "Release" } else { $ENV:CONFIGURATION }

$buildFolder = if ($ENV:APPVEYOR_BUILD_FOLDER -eq $null) { $PSScriptRoot } else { $ENV:APPVEYOR_BUILD_FOLDER }

$openCover = 'C:\ProgramData\chocolatey\lib\opencover.portable\tools\OpenCover.Console.exe'

$target = '-target:C:\Program Files\dotnet\dotnet.exe'
$targetArgs = '-targetargs:"test -c:' + $buildConfig + ' --logger:trx;LogFileName=results.trx --filter FullyQualifiedName!~Benchmarks /p:DebugType=full"' 
$filter = '-filter:+[Kentico.Kontent.Delivery*]*-[*Tests]*-[*Benchmarks]*'
$output = '-output:' + $buildFolder + '\coverage.xml'
$register = if ($ENV:APPVEYOR -eq $true ) { '-register' } else { '-register:user' } # Magical parameter that breaks things

& $openCover $target $targetArgs $filter $register '-oldStyle' '-mergeoutput' $output

Remaining repos

  • kontent-sample-app-net (special: packing x86+x64 website for further internal CI steps)
  • kontent-generators-net (special: packing standalone apps for multiple platforms)

Outstanding tasks

  • fix globally: warning NU5048: The 'PackageIconUrl'/'iconUrl' element is deprecated. Consider using the 'PackageIcon'/'icon' element instead. Learn more at https://aka.ms/deprecateIconUrl

Allow multiple signatures to be defined in your configuration file

Motivation

Webhooks in Kentico Kontent can only define one secret webhook key per URL. As such we have had to make multiple end point definitions in dev / test. In addition due to the issue of no distributed caching ability we have had to make multiple webhook endpoints to identify each node of a load balanced cluster.

This necessitates multiple webhook secrets to be defined in a configuration file.

Here is an example:

image

Proposed solution

We solved this by allowing multiple webhook secrets to be defined as follows:

       public async Task InvokeAsync(HttpContext httpContext, IOptions<ProjectOptionsBase> projectOptions)
        {
            var request = httpContext.Request;
            request.EnableBuffering();

            using var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true);
            var content = await reader.ReadToEndAsync();
            request.Body.Seek(0, SeekOrigin.Begin);

            // Iterates through all secrets to allow us to use multiple Kentico projects content.
            var generatedSignatures = new List<string>();
            foreach (var sig in projectOptions.Value.KenticoKontentWebhookSecrets)
                generatedSignatures.Add(GenerateHash(content, sig));

            var signature = request.Headers["X-KC-Signature"].FirstOrDefault();

            if (!generatedSignatures.Contains(signature))
            {
                httpContext.Response.StatusCode = 401;
                return;
            }

            await _next(httpContext);
        }

Easy fix would be to do the same thing, and make your WebhookOptions model be an array of strings so you could define them in a config file as follows:

  "KenticoKontentWebhookSecrets": [
    ".....", // Matt's ARRT.ORG Ngrok Dev Endpoint
    ".....", // Chris ARRT.ORG NGrok Dev Endpoint
    ".....", //cdevwww2hook.arrt.org
    "....." //tdevwww2hook.arrt.org
  ],

Add new webhook class

Motivation

Why is this feature required? What problems does it solve?
Until last week, we had only webhook, which now is legacy webhook. This webhook used the class DeliveryWebhookModel. We don't have a class for the new webhook which is using different naming for properties.

Proposed solution

An ideal solution for the above problems would be to create a class for the new webhook type so all the projects that use this webhook won't be required to create a new class in each project.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.