Add .NET 7, 8, trimming and AOT support
Also fixed several nullability warnings. Signed-off-by: Giovanni Bassi <giggio@giggio.net>
This commit is contained in:
parent
6f484339e8
commit
6b24309fc9
|
@ -1,6 +1,7 @@
|
||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
@ -15,15 +16,15 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out our repo
|
- name: Check out our repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
# Build with .NET 6.0 SDK
|
# Build with .NET 6.0 SDK
|
||||||
- name: Setup .NET 6.0
|
- name: Setup .NET 8.0
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 6.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out our repo
|
- name: Check out our repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ jobs:
|
||||||
- name: Setup .NET 6.0
|
- name: Setup .NET 6.0
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 6.0.x
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -72,6 +72,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "xml", "xml", "{4012C753-68D
|
||||||
conformance\format\xml\valid-events.xml = conformance\format\xml\valid-events.xml
|
conformance\format\xml\valid-events.xml = conformance\format\xml\valid-events.xml
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpSendJson", "samples\HttpSendJson\HttpSendJson.csproj", "{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudNative.CloudEvents.MinApiSample", "samples\CloudNative.CloudEvents.MinApiSample\CloudNative.CloudEvents.MinApiSample.csproj", "{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -238,15 +244,43 @@ Global
|
||||||
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x64.Build.0 = Release|Any CPU
|
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x86.ActiveCfg = Release|Any CPU
|
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x86.Build.0 = Release|Any CPU
|
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{F1B9B769-DB6B-481F-905C-24FE3B12E00E} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
|
||||||
|
{9760D744-D1BF-40E3-BD6F-7F639BFB9188} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
|
||||||
{A5906FBA-D73A-4A09-8539-CB10D7B586AE} = {8CCC98B3-1776-49FF-96D6-947A9E5DFB0A}
|
{A5906FBA-D73A-4A09-8539-CB10D7B586AE} = {8CCC98B3-1776-49FF-96D6-947A9E5DFB0A}
|
||||||
{D8055631-E6BB-4CD2-8162-F674D6D30E76} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
|
{D8055631-E6BB-4CD2-8162-F674D6D30E76} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
|
||||||
{119AD438-878B-4383-BC9F-779F1605E711} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
|
{119AD438-878B-4383-BC9F-779F1605E711} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
|
||||||
{4012C753-68DE-4737-936F-F5DBC485C51B} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
|
{4012C753-68DE-4737-936F-F5DBC485C51B} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
|
||||||
|
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
|
||||||
|
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {F77A454C-CC17-4AD6-823A-64E1A94FDA0A}
|
SolutionGuid = {F77A454C-CC17-4AD6-823A-64E1A94FDA0A}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\CloudNative.CloudEvents.AspNetCore\CloudNative.CloudEvents.AspNetCore.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\CloudNative.CloudEvents\CloudNative.CloudEvents.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\CloudNative.CloudEvents.SystemTextJson\CloudNative.CloudEvents.SystemTextJson.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
|
<DebugType>None</DebugType>
|
||||||
|
<DebugSymbols>False</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright (c) Cloud Native Foundation.
|
||||||
|
// Licensed under the Apache 2.0 license.
|
||||||
|
// See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using CloudNative.CloudEvents;
|
||||||
|
using CloudNative.CloudEvents.Http;
|
||||||
|
using CloudNative.CloudEvents.SystemTextJson;
|
||||||
|
using CloudNative.CloudEvents.AspNetCore;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
var app = builder.Build();
|
||||||
|
var formatter = new JsonEventFormatter<Message>(MyJsonContext.Default);
|
||||||
|
|
||||||
|
app.MapPost("/api/events/receive/", async (HttpRequest request) =>
|
||||||
|
{
|
||||||
|
var cloudEvent = await request.ToCloudEventAsync(formatter);
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
using var writer = new Utf8JsonWriter(ms, new() { Indented = true });
|
||||||
|
writer.WriteStartObject();
|
||||||
|
foreach (var (attribute, value) in cloudEvent.GetPopulatedAttributes())
|
||||||
|
writer.WriteString(attribute.Name, attribute.Format(value));
|
||||||
|
writer.WriteEndObject();
|
||||||
|
await writer.FlushAsync();
|
||||||
|
var attributeMap = Encoding.UTF8.GetString(ms.ToArray());
|
||||||
|
return Results.Text($"Received event with ID {cloudEvent.Id}, attributes: {attributeMap}");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapPost("/api/events/receive2/", (Event e) => Results.Json(e.CloudEvent.Data, MyJsonContext.Default));
|
||||||
|
|
||||||
|
app.MapPost("/api/events/receive3/", (Message message) => Results.Json(message, MyJsonContext.Default));
|
||||||
|
|
||||||
|
app.MapGet("/api/events/generate/", () =>
|
||||||
|
{
|
||||||
|
var evt = new CloudEvent
|
||||||
|
{
|
||||||
|
Type = "CloudNative.CloudEvents.MinApiSample",
|
||||||
|
Source = new Uri("https://github.com/cloudevents/sdk-csharp"),
|
||||||
|
Time = DateTimeOffset.Now,
|
||||||
|
DataContentType = "application/json",
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
Data = new Message("C#", Environment.Version.ToString())
|
||||||
|
};
|
||||||
|
// Format the event as the body of the response. This is UTF-8 JSON because of
|
||||||
|
// the CloudEventFormatter we're using, but EncodeStructuredModeMessage always
|
||||||
|
// returns binary data. We could return the data directly, but for debugging
|
||||||
|
// purposes it's useful to have the JSON string.
|
||||||
|
var bytes = formatter.EncodeStructuredModeMessage(evt, out var contentType);
|
||||||
|
string json = Encoding.UTF8.GetString(bytes.Span);
|
||||||
|
// Specify the content type of the response: this is what makes it a CloudEvent.
|
||||||
|
// (In "binary mode", the content type is the content type of the data, and headers
|
||||||
|
// indicate that it's a CloudEvent.)
|
||||||
|
return Results.Content(json, contentType.MediaType, Encoding.UTF8);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(Message))]
|
||||||
|
internal partial class MyJsonContext : JsonSerializerContext { }
|
||||||
|
|
||||||
|
public class Event
|
||||||
|
{
|
||||||
|
private readonly static JsonEventFormatter formatter = new JsonEventFormatter<Message>(MyJsonContext.Default);
|
||||||
|
// required for receive2
|
||||||
|
public static async ValueTask<Event?> BindAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var cloudEvent = await context.Request.ToCloudEventAsync(formatter);
|
||||||
|
return new Event { CloudEvent = cloudEvent };
|
||||||
|
}
|
||||||
|
public required CloudEvent CloudEvent { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
record class Message(string Language, string EnvironmentVersion)
|
||||||
|
{
|
||||||
|
private readonly static JsonEventFormatter formatter = new JsonEventFormatter<Message>(MyJsonContext.Default);
|
||||||
|
// required for receive3
|
||||||
|
public static async ValueTask<Message?> BindAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var cloudEvent = await context.Request.ToCloudEventAsync(formatter);
|
||||||
|
return cloudEvent.Data is Message message ? message : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "api/events/generate",
|
||||||
|
"applicationUrl": "http://localhost:5002",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
|
@ -14,5 +14,6 @@
|
||||||
|
|
||||||
<!-- Never pack any sample projects -->
|
<!-- Never pack any sample projects -->
|
||||||
<IsPackable>False</IsPackable>
|
<IsPackable>False</IsPackable>
|
||||||
|
<LangVersion>12.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="docopt.net" Version="0.8.1" />
|
||||||
|
<ProjectReference Include="..\..\src\CloudNative.CloudEvents\CloudNative.CloudEvents.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\CloudNative.CloudEvents.SystemTextJson\CloudNative.CloudEvents.SystemTextJson.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
|
<DebugType>None</DebugType>
|
||||||
|
<DebugSymbols>False</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright (c) Cloud Native Foundation.
|
||||||
|
// Licensed under the Apache 2.0 license.
|
||||||
|
// See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using CloudNative.CloudEvents;
|
||||||
|
using CloudNative.CloudEvents.Http;
|
||||||
|
using CloudNative.CloudEvents.SystemTextJson;
|
||||||
|
using DocoptNet;
|
||||||
|
using System.Net.Mime;
|
||||||
|
using static System.Console;
|
||||||
|
|
||||||
|
// This application uses the docopt.net library for parsing the command
|
||||||
|
// line and calling the application code.
|
||||||
|
ProgramArguments programArguments = new();
|
||||||
|
var result = await ProgramArguments.CreateParserWithVersion()
|
||||||
|
.Parse(args)
|
||||||
|
.Match(RunAsync,
|
||||||
|
result => { WriteLine(result.Help); return Task.FromResult(1); },
|
||||||
|
result => { WriteLine(result.Version); return Task.FromResult(0); },
|
||||||
|
result => { Error.WriteLine(result.Usage); return Task.FromResult(1); });
|
||||||
|
return result;
|
||||||
|
|
||||||
|
static async Task<int> RunAsync(ProgramArguments args)
|
||||||
|
{
|
||||||
|
var cloudEvent = new CloudEvent
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
Type = args.OptType,
|
||||||
|
Source = new Uri(args.OptSource),
|
||||||
|
DataContentType = MediaTypeNames.Application.Json,
|
||||||
|
Data = System.Text.Json.JsonSerializer.Serialize("hey there!", GeneratedJsonContext.Default.String)
|
||||||
|
};
|
||||||
|
|
||||||
|
var content = cloudEvent.ToHttpContent(ContentMode.Structured, new JsonEventFormatter(GeneratedJsonContext.Default));
|
||||||
|
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
// Your application remains in charge of adding any further headers or
|
||||||
|
// other information required to authenticate/authorize or otherwise
|
||||||
|
// dispatch the call at the server.
|
||||||
|
var result = await httpClient.PostAsync(args.OptUrl, content);
|
||||||
|
|
||||||
|
WriteLine(result.StatusCode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Text.Json.Serialization.JsonSerializable(typeof(string))]
|
||||||
|
internal partial class GeneratedJsonContext : System.Text.Json.Serialization.JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[DocoptArguments]
|
||||||
|
partial class ProgramArguments
|
||||||
|
{
|
||||||
|
const string Help = @"HttpSendJson.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
HttpSendJson --url=URL [--type=TYPE] [--source=SOURCE]
|
||||||
|
HttpSendJson (-h | --help)
|
||||||
|
HttpSendJson --version
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--url=URL HTTP(S) address to send the event to.
|
||||||
|
--type=TYPE CloudEvents 'type' [default: com.example.myevent].
|
||||||
|
--source=SOURCE CloudEvents 'source' [default: urn:example-com:mysource:abc].
|
||||||
|
-h --help Show this screen.
|
||||||
|
--version Show version.
|
||||||
|
";
|
||||||
|
public static string Version => $"producer {typeof(ProgramArguments).Assembly.GetName().Version}";
|
||||||
|
public static IParser<ProgramArguments> CreateParserWithVersion() => CreateParser().WithVersion(Version);
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ using Amqp.Types;
|
||||||
using CloudNative.CloudEvents.Core;
|
using CloudNative.CloudEvents.Core;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ namespace CloudNative.CloudEvents.Amqp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasCloudEventsContentType(Message message, out string? contentType)
|
private static bool HasCloudEventsContentType(Message message, [NotNullWhen(true)] out string? contentType)
|
||||||
{
|
{
|
||||||
contentType = message.Properties.ContentType?.ToString();
|
contentType = message.Properties.ContentType?.ToString();
|
||||||
return MimeUtilities.IsCloudEventsContentType(contentType);
|
return MimeUtilities.IsCloudEventsContentType(contentType);
|
||||||
|
@ -250,3 +251,22 @@ namespace CloudNative.CloudEvents.Amqp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NETSTANDARD2_0
|
||||||
|
namespace System.Diagnostics.CodeAnalysis
|
||||||
|
{
|
||||||
|
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||||
|
internal sealed class NotNullWhenAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>Initializes the attribute with the specified return value condition.</summary>
|
||||||
|
/// <param name="returnValue">
|
||||||
|
/// The return value condition. If the method returns this value, the associated parameter will not be null.
|
||||||
|
/// </param>
|
||||||
|
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
|
||||||
|
|
||||||
|
/// <summary>Gets the return value condition.</summary>
|
||||||
|
public bool ReturnValue { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -1,9 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>AMQP extensions for CloudNative.CloudEvents</Description>
|
<Description>AMQP extensions for CloudNative.CloudEvents</Description>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;amqp</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;amqp</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>ASP.Net Core extensions for CloudNative.CloudEvents</Description>
|
<Description>ASP.Net Core extensions for CloudNative.CloudEvents</Description>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;aspnetcore;aspnet</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;aspnetcore;aspnet</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -185,7 +185,7 @@ namespace CloudNative.CloudEvents.Avro
|
||||||
// will fail and that's okay since the type is useless without the proper schema.
|
// will fail and that's okay since the type is useless without the proper schema.
|
||||||
using var sr = new StreamReader(typeof(AvroEventFormatter)
|
using var sr = new StreamReader(typeof(AvroEventFormatter)
|
||||||
.Assembly
|
.Assembly
|
||||||
.GetManifestResourceStream("CloudNative.CloudEvents.Avro.AvroSchema.json"));
|
.GetManifestResourceStream("CloudNative.CloudEvents.Avro.AvroSchema.json")!);
|
||||||
|
|
||||||
return (RecordSchema) Schema.Parse(sr.ReadToEnd());
|
return (RecordSchema) Schema.Parse(sr.ReadToEnd());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>Avro extensions for CloudNative.CloudEvents</Description>
|
<Description>Avro extensions for CloudNative.CloudEvents</Description>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;avro</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;avro</PackageTags>
|
||||||
<LangVersion>10.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>Kafka extensions for CloudNative.CloudEvents</Description>
|
<Description>Kafka extensions for CloudNative.CloudEvents</Description>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;kafka</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;kafka</PackageTags>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>MQTT extensions for CloudNative.CloudEvents</Description>
|
<Description>MQTT extensions for CloudNative.CloudEvents</Description>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;mqtt</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;mqtt</PackageTags>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>JSON support for the CNCF CloudEvents SDK, based on Newtonsoft.Json.</Description>
|
<Description>JSON support for the CNCF CloudEvents SDK, based on Newtonsoft.Json.</Description>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;json;newtonsoft</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;json;newtonsoft</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -345,7 +345,9 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Structured mode property '{DataBase64PropertyName}' must be a string, when present.");
|
throw new ArgumentException($"Structured mode property '{DataBase64PropertyName}' must be a string, when present.");
|
||||||
}
|
}
|
||||||
cloudEvent.Data = Convert.FromBase64String((string?)dataBase64Token);
|
var tokenString = (string?)dataBase64Token;
|
||||||
|
if (tokenString != null)
|
||||||
|
cloudEvent.Data = Convert.FromBase64String(tokenString);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -524,7 +526,7 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ContentType dataContentType = new ContentType(GetOrInferDataContentType(cloudEvent));
|
ContentType dataContentType = new ContentType(GetOrInferDataContentType(cloudEvent)!);
|
||||||
if (IsJsonMediaType(dataContentType.MediaType))
|
if (IsJsonMediaType(dataContentType.MediaType))
|
||||||
{
|
{
|
||||||
writer.WritePropertyName(DataPropertyName);
|
writer.WritePropertyName(DataPropertyName);
|
||||||
|
@ -696,7 +698,7 @@ namespace CloudNative.CloudEvents.NewtonsoftJson
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void EncodeStructuredModeData(CloudEvent cloudEvent, JsonWriter writer)
|
protected override void EncodeStructuredModeData(CloudEvent cloudEvent, JsonWriter writer)
|
||||||
{
|
{
|
||||||
T data = (T)cloudEvent.Data;
|
var data = (T?)cloudEvent.Data;
|
||||||
writer.WritePropertyName(DataPropertyName);
|
writer.WritePropertyName(DataPropertyName);
|
||||||
Serializer.Serialize(writer, data);
|
Serializer.Serialize(writer, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>Support for the Protobuf event format in for CloudNative.CloudEvents</Description>
|
<Description>Support for the Protobuf event format in for CloudNative.CloudEvents</Description>
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;protobuf</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;protobuf</PackageTags>
|
||||||
<LangVersion>10.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>JSON support for the CNCF CloudEvents SDK, based on System.Text.Json.</Description>
|
<Description>JSON support for the CNCF CloudEvents SDK, based on System.Text.Json.</Description>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<PackageTags>cncf;cloudnative;cloudevents;events;json;systemtextjson</PackageTags>
|
<PackageTags>cncf;cloudnative;cloudevents;events;json;systemtextjson</PackageTags>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
using CloudNative.CloudEvents.Core;
|
using CloudNative.CloudEvents.Core;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
@ -94,6 +96,13 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected const string DataPropertyName = "data";
|
protected const string DataPropertyName = "data";
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
/// <summary>
|
||||||
|
/// Json serialization context used to serialize and enable trimming and AOT.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly JsonSerializerContext? JsonSerializerContext;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The options to use when serializing objects to JSON.
|
/// The options to use when serializing objects to JSON.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -108,22 +117,54 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
/// Creates a JsonEventFormatter that uses the default <see cref="JsonSerializerOptions"/>
|
/// Creates a JsonEventFormatter that uses the default <see cref="JsonSerializerOptions"/>
|
||||||
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[RequiresUnreferencedCode("Use a constructor that takes a JsonSerializerContext.")]
|
||||||
|
#endif
|
||||||
public JsonEventFormatter() : this(null, default)
|
public JsonEventFormatter() : this(null, default)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a JsonEventFormatter that uses the default <see cref="JsonSerializerOptions"/>
|
||||||
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonSerializerContext">The json context used for serializing objects to JSON.</param>
|
||||||
|
public JsonEventFormatter(JsonSerializerContext jsonSerializerContext) : this(default, jsonSerializerContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a JsonEventFormatter that uses the specified <see cref="JsonSerializerOptions"/>
|
/// Creates a JsonEventFormatter that uses the specified <see cref="JsonSerializerOptions"/>
|
||||||
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serializerOptions">The options to use when serializing objects to JSON. May be null.</param>
|
/// <param name="serializerOptions">The options to use when serializing objects to JSON. May be null.</param>
|
||||||
/// <param name="documentOptions">The options to use when parsing JSON documents.</param>
|
/// <param name="documentOptions">The options to use when parsing JSON documents.</param>
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[RequiresUnreferencedCode("Use a constructor that takes a JsonSerializerContext.")]
|
||||||
|
#endif
|
||||||
public JsonEventFormatter(JsonSerializerOptions? serializerOptions, JsonDocumentOptions documentOptions)
|
public JsonEventFormatter(JsonSerializerOptions? serializerOptions, JsonDocumentOptions documentOptions)
|
||||||
{
|
{
|
||||||
SerializerOptions = serializerOptions;
|
SerializerOptions = serializerOptions;
|
||||||
DocumentOptions = documentOptions;
|
DocumentOptions = documentOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a JsonEventFormatter that uses the specified <see cref="JsonSerializerOptions"/>
|
||||||
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="documentOptions">The options to use when parsing JSON documents.</param>
|
||||||
|
/// <param name="jsonSerializerContext">The json context used for serializing objects to JSON.</param>
|
||||||
|
public JsonEventFormatter(JsonDocumentOptions documentOptions, JsonSerializerContext jsonSerializerContext)
|
||||||
|
{
|
||||||
|
Validation.CheckNotNull(jsonSerializerContext, nameof(jsonSerializerContext));
|
||||||
|
DocumentOptions = documentOptions;
|
||||||
|
JsonSerializerContext = jsonSerializerContext;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<CloudEvent> DecodeStructuredModeMessageAsync(Stream body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes) =>
|
public override async Task<CloudEvent> DecodeStructuredModeMessageAsync(Stream body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes) =>
|
||||||
await DecodeStructuredModeMessageImpl(body, contentType, extensionAttributes, true).ConfigureAwait(false);
|
await DecodeStructuredModeMessageImpl(body, contentType, extensionAttributes, true).ConfigureAwait(false);
|
||||||
|
@ -533,6 +574,10 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
/// <param name="cloudEvent">The CloudEvent being encoded, which will have a non-null value for
|
/// <param name="cloudEvent">The CloudEvent being encoded, which will have a non-null value for
|
||||||
/// its <see cref="CloudEvent.Data"/> property.
|
/// its <see cref="CloudEvent.Data"/> property.
|
||||||
/// <param name="writer"/>The writer to serialize the data to. Will not be null.</param>
|
/// <param name="writer"/>The writer to serialize the data to. Will not be null.</param>
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Constructor already annotated.")]
|
||||||
|
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Constructor already annotated.")]
|
||||||
|
#endif
|
||||||
protected virtual void EncodeStructuredModeData(CloudEvent cloudEvent, Utf8JsonWriter writer)
|
protected virtual void EncodeStructuredModeData(CloudEvent cloudEvent, Utf8JsonWriter writer)
|
||||||
{
|
{
|
||||||
// Binary data is encoded using the data_base64 property, regardless of content type.
|
// Binary data is encoded using the data_base64 property, regardless of content type.
|
||||||
|
@ -548,7 +593,12 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
if (IsJsonMediaType(dataContentType.MediaType))
|
if (IsJsonMediaType(dataContentType.MediaType))
|
||||||
{
|
{
|
||||||
writer.WritePropertyName(DataPropertyName);
|
writer.WritePropertyName(DataPropertyName);
|
||||||
JsonSerializer.Serialize(writer, cloudEvent.Data, SerializerOptions);
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
JsonSerializer.Serialize(writer, cloudEvent.Data, cloudEvent.Data!.GetType(), JsonSerializerContext);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
JsonSerializer.Serialize(writer, cloudEvent.Data, SerializerOptions);
|
||||||
}
|
}
|
||||||
else if (cloudEvent.Data is string text && dataContentType.MediaType.StartsWith("text/"))
|
else if (cloudEvent.Data is string text && dataContentType.MediaType.StartsWith("text/"))
|
||||||
{
|
{
|
||||||
|
@ -564,6 +614,10 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Constructor already annotated.")]
|
||||||
|
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Constructor already annotated.")]
|
||||||
|
#endif
|
||||||
public override ReadOnlyMemory<byte> EncodeBinaryModeEventData(CloudEvent cloudEvent)
|
public override ReadOnlyMemory<byte> EncodeBinaryModeEventData(CloudEvent cloudEvent)
|
||||||
{
|
{
|
||||||
Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent));
|
Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent));
|
||||||
|
@ -584,10 +638,18 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
var encoding = MimeUtilities.GetEncoding(contentType);
|
var encoding = MimeUtilities.GetEncoding(contentType);
|
||||||
if (encoding is UTF8Encoding)
|
if (encoding is UTF8Encoding)
|
||||||
{
|
{
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
return JsonSerializer.SerializeToUtf8Bytes(cloudEvent.Data, cloudEvent.Data.GetType(), JsonSerializerContext);
|
||||||
|
#endif
|
||||||
return JsonSerializer.SerializeToUtf8Bytes(cloudEvent.Data, SerializerOptions);
|
return JsonSerializer.SerializeToUtf8Bytes(cloudEvent.Data, SerializerOptions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
return MimeUtilities.GetEncoding(contentType).GetBytes(JsonSerializer.Serialize(cloudEvent.Data, cloudEvent.Data.GetType(), JsonSerializerContext));
|
||||||
|
#endif
|
||||||
return MimeUtilities.GetEncoding(contentType).GetBytes(JsonSerializer.Serialize(cloudEvent.Data, SerializerOptions));
|
return MimeUtilities.GetEncoding(contentType).GetBytes(JsonSerializer.Serialize(cloudEvent.Data, SerializerOptions));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -654,22 +716,57 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
/// Creates a JsonEventFormatter that uses the default <see cref="JsonSerializerOptions"/>
|
/// Creates a JsonEventFormatter that uses the default <see cref="JsonSerializerOptions"/>
|
||||||
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[RequiresUnreferencedCode("Use a constructor that takes a JsonSerializerContext.")]
|
||||||
|
#endif
|
||||||
public JsonEventFormatter()
|
public JsonEventFormatter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a JsonEventFormatter that uses the serializer <see cref="JsonSerializerOptions"/>
|
||||||
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonSerializerContext">The json context used for serializing objects to JSON.</param>
|
||||||
|
public JsonEventFormatter(JsonSerializerContext jsonSerializerContext)
|
||||||
|
: base(default, jsonSerializerContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a JsonEventFormatter that uses the serializer <see cref="JsonSerializerOptions"/>
|
/// Creates a JsonEventFormatter that uses the serializer <see cref="JsonSerializerOptions"/>
|
||||||
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serializerOptions">The options to use when serializing and parsing. May be null.</param>
|
/// <param name="serializerOptions">The options to use when serializing and parsing. May be null.</param>
|
||||||
/// <param name="documentOptions">The options to use when parsing JSON documents.</param>
|
/// <param name="documentOptions">The options to use when parsing JSON documents.</param>
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[RequiresUnreferencedCode("Use a constructor that takes a JsonSerializerContext.")]
|
||||||
|
#endif
|
||||||
public JsonEventFormatter(JsonSerializerOptions serializerOptions, JsonDocumentOptions documentOptions)
|
public JsonEventFormatter(JsonSerializerOptions serializerOptions, JsonDocumentOptions documentOptions)
|
||||||
: base(serializerOptions, documentOptions)
|
: base(serializerOptions, documentOptions)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a JsonEventFormatter that uses the serializer <see cref="JsonSerializerOptions"/>
|
||||||
|
/// and <see cref="JsonDocumentOptions"/> for serializing and parsing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="documentOptions">The options to use when parsing JSON documents.</param>
|
||||||
|
/// <param name="jsonSerializerContext">The json context used for serializing objects to JSON.</param>
|
||||||
|
public JsonEventFormatter(JsonDocumentOptions documentOptions, JsonSerializerContext jsonSerializerContext)
|
||||||
|
: base(documentOptions, jsonSerializerContext)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Constructor already annotated.")]
|
||||||
|
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Constructor already annotated.")]
|
||||||
|
#endif
|
||||||
public override ReadOnlyMemory<byte> EncodeBinaryModeEventData(CloudEvent cloudEvent)
|
public override ReadOnlyMemory<byte> EncodeBinaryModeEventData(CloudEvent cloudEvent)
|
||||||
{
|
{
|
||||||
Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent));
|
Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent));
|
||||||
|
@ -679,10 +776,18 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
return Array.Empty<byte>();
|
return Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
T data = (T)cloudEvent.Data;
|
T data = (T)cloudEvent.Data;
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
return JsonSerializer.SerializeToUtf8Bytes(data, data.GetType(), JsonSerializerContext);
|
||||||
|
#endif
|
||||||
return JsonSerializer.SerializeToUtf8Bytes(data, SerializerOptions);
|
return JsonSerializer.SerializeToUtf8Bytes(data, SerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Constructor already annotated.")]
|
||||||
|
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Constructor already annotated.")]
|
||||||
|
#endif
|
||||||
public override void DecodeBinaryModeEventData(ReadOnlyMemory<byte> body, CloudEvent cloudEvent)
|
public override void DecodeBinaryModeEventData(ReadOnlyMemory<byte> body, CloudEvent cloudEvent)
|
||||||
{
|
{
|
||||||
Validation.CheckNotNull(cloudEvent, nameof(cloudEvent));
|
Validation.CheckNotNull(cloudEvent, nameof(cloudEvent));
|
||||||
|
@ -692,22 +797,45 @@ namespace CloudNative.CloudEvents.SystemTextJson
|
||||||
cloudEvent.Data = null;
|
cloudEvent.Data = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cloudEvent.Data = JsonSerializer.Deserialize<T>(body.Span, SerializerOptions);
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
cloudEvent.Data = JsonSerializer.Deserialize(body.Span, typeof(T), JsonSerializerContext);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
cloudEvent.Data = JsonSerializer.Deserialize<T>(body.Span, SerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Constructor already annotated.")]
|
||||||
|
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Constructor already annotated.")]
|
||||||
|
#endif
|
||||||
protected override void EncodeStructuredModeData(CloudEvent cloudEvent, Utf8JsonWriter writer)
|
protected override void EncodeStructuredModeData(CloudEvent cloudEvent, Utf8JsonWriter writer)
|
||||||
{
|
{
|
||||||
T data = (T)cloudEvent.Data;
|
var data = (T?)cloudEvent.Data;
|
||||||
writer.WritePropertyName(DataPropertyName);
|
writer.WritePropertyName(DataPropertyName);
|
||||||
JsonSerializer.Serialize(writer, data, SerializerOptions);
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
JsonSerializer.Serialize(writer, data, data!.GetType(), JsonSerializerContext);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
JsonSerializer.Serialize(writer, data, SerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void DecodeStructuredModeDataProperty(JsonElement dataElement, CloudEvent cloudEvent) =>
|
#if NET7_0_OR_GREATER
|
||||||
// Note: this is an inefficient way of doing this.
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Constructor already annotated.")]
|
||||||
// See https://github.com/dotnet/runtime/issues/31274 - when that's implemented, we can use the new method here.
|
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Constructor already annotated.")]
|
||||||
cloudEvent.Data = JsonSerializer.Deserialize<T>(dataElement.GetRawText(), SerializerOptions);
|
#endif
|
||||||
|
protected override void DecodeStructuredModeDataProperty(JsonElement dataElement, CloudEvent cloudEvent)
|
||||||
|
{
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
if (JsonSerializerContext != null)
|
||||||
|
cloudEvent.Data = JsonSerializer.Deserialize(dataElement, typeof(T), JsonSerializerContext);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
cloudEvent.Data = JsonSerializer.Deserialize<T>(dataElement.GetRawText(), SerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Consider decoding the base64 data as a byte array, then using DecodeBinaryModeData.
|
// TODO: Consider decoding the base64 data as a byte array, then using DecodeBinaryModeData.
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
using CloudNative.CloudEvents.Core;
|
using CloudNative.CloudEvents.Core;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace CloudNative.CloudEvents
|
namespace CloudNative.CloudEvents
|
||||||
|
@ -21,6 +22,9 @@ namespace CloudNative.CloudEvents
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type to use for CloudEvent formatting. Must not be null.
|
/// The type to use for CloudEvent formatting. Must not be null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
|
||||||
|
#endif
|
||||||
public Type FormatterType { get; }
|
public Type FormatterType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -56,7 +60,7 @@ namespace CloudNative.CloudEvents
|
||||||
throw new ArgumentException($"The {nameof(CloudEventFormatterAttribute)} on type {targetType} has no converter type specified.", nameof(targetType));
|
throw new ArgumentException($"The {nameof(CloudEventFormatterAttribute)} on type {targetType} has no converter type specified.", nameof(targetType));
|
||||||
}
|
}
|
||||||
|
|
||||||
object instance;
|
object? instance;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
instance = Activator.CreateInstance(formatterType);
|
instance = Activator.CreateInstance(formatterType);
|
||||||
|
@ -73,6 +77,6 @@ namespace CloudNative.CloudEvents
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||||
<Description>CNCF CloudEvents SDK</Description>
|
<Description>CNCF CloudEvents SDK</Description>
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PackageTags>cloudnative;cloudevents;events</PackageTags>
|
<PackageTags>cloudnative;cloudevents;events</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
// It's safe to use memory.GetBuffer() and memory.Position here, as this is a stream
|
// It's safe to use memory.GetBuffer() and memory.Position here, as this is a stream
|
||||||
// we've created using the parameterless constructor.
|
// we've created using the parameterless constructor.
|
||||||
var buffer = memory.GetBuffer();
|
var buffer = memory.GetBuffer();
|
||||||
return new ReadOnlyMemory<byte>(buffer, 0, (int) memory.Position);
|
return new ReadOnlyMemory<byte>(buffer, 0, (int)memory.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -65,7 +65,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
public static MemoryStream AsStream(ReadOnlyMemory<byte> memory)
|
public static MemoryStream AsStream(ReadOnlyMemory<byte> memory)
|
||||||
{
|
{
|
||||||
var segment = GetArraySegment(memory);
|
var segment = GetArraySegment(memory);
|
||||||
return new MemoryStream(segment.Array, segment.Offset, segment.Count, false);
|
return new MemoryStream(segment.Array!, segment.Offset, segment.Count, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -79,7 +79,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
|
|
||||||
// TODO: If we introduce an additional netstandard2.1 target, we can use encoding.GetString(memory.Span)
|
// TODO: If we introduce an additional netstandard2.1 target, we can use encoding.GetString(memory.Span)
|
||||||
var segment = GetArraySegment(memory);
|
var segment = GetArraySegment(memory);
|
||||||
return encoding.GetString(segment.Array, segment.Offset, segment.Count);
|
return encoding.GetString(segment.Array!, segment.Offset, segment.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -92,7 +92,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
{
|
{
|
||||||
Validation.CheckNotNull(destination, nameof(destination));
|
Validation.CheckNotNull(destination, nameof(destination));
|
||||||
var segment = GetArraySegment(source);
|
var segment = GetArraySegment(source);
|
||||||
await destination.WriteAsync(segment.Array, segment.Offset, segment.Count).ConfigureAwait(false);
|
await destination.WriteAsync(segment.Array!, segment.Offset, segment.Count).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -108,7 +108,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
var segment = GetArraySegment(memory);
|
var segment = GetArraySegment(memory);
|
||||||
// We probably don't actually need to check the offset: if the count is the same as the length,
|
// We probably don't actually need to check the offset: if the count is the same as the length,
|
||||||
// I can't see how the offset can be non-zero. But it doesn't *hurt* as a check.
|
// I can't see how the offset can be non-zero. But it doesn't *hurt* as a check.
|
||||||
return segment.Offset == 0 && segment.Count == segment.Array.Length
|
return segment.Array is not null && segment.Offset == 0 && segment.Count == segment.Array.Length
|
||||||
? segment.Array
|
? segment.Array
|
||||||
: memory.ToArray();
|
: memory.ToArray();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// See LICENSE file in the project root for full license information.
|
// See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -57,7 +58,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
var header = new MediaTypeHeaderValue(contentType.MediaType);
|
var header = new MediaTypeHeaderValue(contentType.MediaType);
|
||||||
foreach (string parameterName in contentType.Parameters.Keys)
|
foreach (string parameterName in contentType.Parameters.Keys)
|
||||||
{
|
{
|
||||||
header.Parameters.Add(new NameValueHeaderValue(parameterName, contentType.Parameters[parameterName].ToString()));
|
header.Parameters.Add(new NameValueHeaderValue(parameterName, contentType.Parameters[parameterName]!.ToString()));
|
||||||
}
|
}
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="contentType">The content type to check. May be null, in which case the result is false.</param>
|
/// <param name="contentType">The content type to check. May be null, in which case the result is false.</param>
|
||||||
/// <returns>true if the given content type denotes a (non-batch) CloudEvent; false otherwise</returns>
|
/// <returns>true if the given content type denotes a (non-batch) CloudEvent; false otherwise</returns>
|
||||||
public static bool IsCloudEventsContentType(string? contentType) =>
|
public static bool IsCloudEventsContentType([NotNullWhen(true)] string? contentType) =>
|
||||||
contentType is string &&
|
contentType is string &&
|
||||||
contentType.StartsWith(MediaType, StringComparison.InvariantCultureIgnoreCase) &&
|
contentType.StartsWith(MediaType, StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
!contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase);
|
!contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
@ -86,7 +87,7 @@ namespace CloudNative.CloudEvents.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="contentType">The content type to check. May be null, in which case the result is false.</param>
|
/// <param name="contentType">The content type to check. May be null, in which case the result is false.</param>
|
||||||
/// <returns>true if the given content type represents a CloudEvent batch; false otherwise</returns>
|
/// <returns>true if the given content type represents a CloudEvent batch; false otherwise</returns>
|
||||||
public static bool IsCloudEventsBatchContentType(string? contentType) =>
|
public static bool IsCloudEventsBatchContentType([NotNullWhen(true)] string? contentType) =>
|
||||||
contentType is string && contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase);
|
contentType is string && contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
using CloudNative.CloudEvents.Core;
|
using CloudNative.CloudEvents.Core;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
@ -61,6 +62,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
return HasCloudEventsBatchContentType(httpRequestMessage.Content);
|
return HasCloudEventsBatchContentType(httpRequestMessage.Content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether this <see cref="HttpResponseMessage"/> holds a batch of CloudEvents.
|
/// Indicates whether this <see cref="HttpResponseMessage"/> holds a batch of CloudEvents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -83,7 +85,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
this HttpResponseMessage httpResponseMessage,
|
this HttpResponseMessage httpResponseMessage,
|
||||||
CloudEventFormatter formatter,
|
CloudEventFormatter formatter,
|
||||||
params CloudEventAttribute[]? extensionAttributes) =>
|
params CloudEventAttribute[]? extensionAttributes) =>
|
||||||
ToCloudEventAsync(httpResponseMessage, formatter, (IEnumerable<CloudEventAttribute>?) extensionAttributes);
|
ToCloudEventAsync(httpResponseMessage, formatter, (IEnumerable<CloudEventAttribute>?)extensionAttributes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts this HTTP response message into a CloudEvent object
|
/// Converts this HTTP response message into a CloudEvent object
|
||||||
|
@ -112,7 +114,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
this HttpRequestMessage httpRequestMessage,
|
this HttpRequestMessage httpRequestMessage,
|
||||||
CloudEventFormatter formatter,
|
CloudEventFormatter formatter,
|
||||||
params CloudEventAttribute[]? extensionAttributes) =>
|
params CloudEventAttribute[]? extensionAttributes) =>
|
||||||
ToCloudEventAsync(httpRequestMessage, formatter, (IEnumerable<CloudEventAttribute>?) extensionAttributes);
|
ToCloudEventAsync(httpRequestMessage, formatter, (IEnumerable<CloudEventAttribute>?)extensionAttributes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts this HTTP request message into a CloudEvent object.
|
/// Converts this HTTP request message into a CloudEvent object.
|
||||||
|
@ -130,7 +132,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
return ToCloudEventInternalAsync(httpRequestMessage.Headers, httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage));
|
return ToCloudEventInternalAsync(httpRequestMessage.Headers, httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content,
|
private static async Task<CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent? content,
|
||||||
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, string paramName)
|
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, string paramName)
|
||||||
{
|
{
|
||||||
Validation.CheckNotNull(formatter, nameof(formatter));
|
Validation.CheckNotNull(formatter, nameof(formatter));
|
||||||
|
@ -142,7 +144,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content.Headers);
|
string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content?.Headers);
|
||||||
if (versionId is null)
|
if (versionId is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(paramName));
|
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(paramName));
|
||||||
|
@ -151,7 +153,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);
|
?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);
|
||||||
|
|
||||||
var cloudEvent = new CloudEvent(version, extensionAttributes);
|
var cloudEvent = new CloudEvent(version, extensionAttributes);
|
||||||
foreach (var header in headers.Concat(content.Headers))
|
foreach (var header in headers.Concat(content!.Headers))
|
||||||
{
|
{
|
||||||
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
|
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
|
||||||
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
||||||
|
@ -231,7 +233,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
return ToCloudEventBatchInternalAsync(httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage));
|
return ToCloudEventBatchInternalAsync(httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IReadOnlyList<CloudEvent>> ToCloudEventBatchInternalAsync(HttpContent content,
|
private static async Task<IReadOnlyList<CloudEvent>> ToCloudEventBatchInternalAsync(HttpContent? content,
|
||||||
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, string paramName)
|
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, string paramName)
|
||||||
{
|
{
|
||||||
Validation.CheckNotNull(formatter, nameof(formatter));
|
Validation.CheckNotNull(formatter, nameof(formatter));
|
||||||
|
@ -332,15 +334,15 @@ namespace CloudNative.CloudEvents.Http
|
||||||
|
|
||||||
private static ByteArrayContent ToByteArrayContent(ReadOnlyMemory<byte> content) =>
|
private static ByteArrayContent ToByteArrayContent(ReadOnlyMemory<byte> content) =>
|
||||||
MemoryMarshal.TryGetArray(content, out var segment)
|
MemoryMarshal.TryGetArray(content, out var segment)
|
||||||
? new ByteArrayContent(segment.Array, segment.Offset, segment.Count)
|
? new ByteArrayContent(segment.Array!, segment.Offset, segment.Count)
|
||||||
// TODO: Just throw?
|
// TODO: Just throw?
|
||||||
: new ByteArrayContent(content.ToArray());
|
: new ByteArrayContent(content.ToArray());
|
||||||
|
|
||||||
// TODO: This would include "application/cloudeventsarerubbish" for example...
|
// TODO: This would include "application/cloudeventsarerubbish" for example...
|
||||||
private static bool HasCloudEventsContentType(HttpContent content) =>
|
private static bool HasCloudEventsContentType([NotNullWhen(true)] HttpContent? content) =>
|
||||||
MimeUtilities.IsCloudEventsContentType(content?.Headers?.ContentType?.MediaType);
|
MimeUtilities.IsCloudEventsContentType(content?.Headers?.ContentType?.MediaType);
|
||||||
|
|
||||||
private static bool HasCloudEventsBatchContentType(HttpContent content) =>
|
private static bool HasCloudEventsBatchContentType([NotNullWhen(true)] HttpContent? content) =>
|
||||||
MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType);
|
MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType);
|
||||||
|
|
||||||
private static string? MaybeGetVersionId(HttpHeaders? headers) =>
|
private static string? MaybeGetVersionId(HttpHeaders? headers) =>
|
||||||
|
|
|
@ -151,7 +151,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
/// <returns>A reference to a validated CloudEvent instance.</returns>
|
/// <returns>A reference to a validated CloudEvent instance.</returns>
|
||||||
public static CloudEvent ToCloudEvent(this HttpListenerRequest httpListenerRequest,
|
public static CloudEvent ToCloudEvent(this HttpListenerRequest httpListenerRequest,
|
||||||
CloudEventFormatter formatter, params CloudEventAttribute[]? extensionAttributes) =>
|
CloudEventFormatter formatter, params CloudEventAttribute[]? extensionAttributes) =>
|
||||||
ToCloudEvent(httpListenerRequest, formatter, (IEnumerable<CloudEventAttribute>?) extensionAttributes);
|
ToCloudEvent(httpListenerRequest, formatter, (IEnumerable<CloudEventAttribute>?)extensionAttributes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts this listener request into a CloudEvent object, with the given extension attributes.
|
/// Converts this listener request into a CloudEvent object, with the given extension attributes.
|
||||||
|
@ -179,7 +179,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader];
|
string? versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader];
|
||||||
if (versionId is null)
|
if (versionId is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpListenerRequest));
|
throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpListenerRequest));
|
||||||
|
@ -191,12 +191,12 @@ namespace CloudNative.CloudEvents.Http
|
||||||
var headers = httpListenerRequest.Headers;
|
var headers = httpListenerRequest.Headers;
|
||||||
foreach (var key in headers.AllKeys)
|
foreach (var key in headers.AllKeys)
|
||||||
{
|
{
|
||||||
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(key);
|
string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(key!);
|
||||||
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
string attributeValue = HttpUtilities.DecodeHeaderValue(headers[key]);
|
string attributeValue = HttpUtilities.DecodeHeaderValue(headers[key]!);
|
||||||
cloudEvent.SetAttributeFromString(attributeName, attributeValue);
|
cloudEvent.SetAttributeFromString(attributeName, attributeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
this HttpListenerRequest httpListenerRequest,
|
this HttpListenerRequest httpListenerRequest,
|
||||||
CloudEventFormatter formatter,
|
CloudEventFormatter formatter,
|
||||||
params CloudEventAttribute[]? extensionAttributes) =>
|
params CloudEventAttribute[]? extensionAttributes) =>
|
||||||
ToCloudEventBatchAsync(httpListenerRequest, formatter, (IEnumerable<CloudEventAttribute>?)extensionAttributes);
|
ToCloudEventBatchAsync(httpListenerRequest, formatter, (IEnumerable<CloudEventAttribute>?) extensionAttributes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts this HTTP request message into a CloudEvent batch.
|
/// Converts this HTTP request message into a CloudEvent batch.
|
||||||
|
@ -264,6 +264,7 @@ namespace CloudNative.CloudEvents.Http
|
||||||
IEnumerable<CloudEventAttribute>? extensionAttributes) =>
|
IEnumerable<CloudEventAttribute>? extensionAttributes) =>
|
||||||
ToCloudEventBatchInternalAsync(httpListenerRequest, formatter, extensionAttributes, async: false).GetAwaiter().GetResult();
|
ToCloudEventBatchInternalAsync(httpListenerRequest, formatter, extensionAttributes, async: false).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
|
||||||
private async static Task<IReadOnlyList<CloudEvent>> ToCloudEventBatchInternalAsync(HttpListenerRequest httpListenerRequest,
|
private async static Task<IReadOnlyList<CloudEvent>> ToCloudEventBatchInternalAsync(HttpListenerRequest httpListenerRequest,
|
||||||
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, bool async)
|
CloudEventFormatter formatter, IEnumerable<CloudEventAttribute>? extensionAttributes, bool async)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
<PackageProjectUrl>https://cloudevents.io</PackageProjectUrl>
|
<PackageProjectUrl>https://cloudevents.io</PackageProjectUrl>
|
||||||
<Copyright>Copyright Cloud Native Foundation</Copyright>
|
<Copyright>Copyright Cloud Native Foundation</Copyright>
|
||||||
|
|
||||||
|
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">true</IsTrimmable>
|
||||||
|
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
|
||||||
|
<LangVersion>12.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Package the icon specified in the PackageIcon property -->
|
<!-- Package the icon specified in the PackageIcon property -->
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
using CloudNative.CloudEvents.UnitTests;
|
using CloudNative.CloudEvents.UnitTests;
|
||||||
using CloudNative.CloudEvents.UnitTests.ConformanceTestData;
|
using CloudNative.CloudEvents.UnitTests.ConformanceTestData;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -21,7 +22,8 @@ public class ConformanceTest
|
||||||
private static IEnumerable<object[]> SelectTestIds(ConformanceTestType type) =>
|
private static IEnumerable<object[]> SelectTestIds(ConformanceTestType type) =>
|
||||||
allTests
|
allTests
|
||||||
.Where(test => test.TestType == type)
|
.Where(test => test.TestType == type)
|
||||||
.Select(test => new object[] { test.Id });
|
.Select(test => new object[][] { [test.Id, true], [test.Id, false] })
|
||||||
|
.SelectMany(x => x);
|
||||||
|
|
||||||
public static IEnumerable<object[]> ValidEventTestIds => SelectTestIds(ConformanceTestType.ValidSingleEvent);
|
public static IEnumerable<object[]> ValidEventTestIds => SelectTestIds(ConformanceTestType.ValidSingleEvent);
|
||||||
public static IEnumerable<object[]> InvalidEventTestIds => SelectTestIds(ConformanceTestType.InvalidSingleEvent);
|
public static IEnumerable<object[]> InvalidEventTestIds => SelectTestIds(ConformanceTestType.InvalidSingleEvent);
|
||||||
|
@ -29,20 +31,21 @@ public class ConformanceTest
|
||||||
public static IEnumerable<object[]> InvalidBatchTestIds => SelectTestIds(ConformanceTestType.InvalidBatch);
|
public static IEnumerable<object[]> InvalidBatchTestIds => SelectTestIds(ConformanceTestType.InvalidBatch);
|
||||||
|
|
||||||
[Theory, MemberData(nameof(ValidEventTestIds))]
|
[Theory, MemberData(nameof(ValidEventTestIds))]
|
||||||
public void ValidEvent(string testId)
|
public void ValidEvent(string testId, bool useContext)
|
||||||
{
|
{
|
||||||
var test = GetTestById(testId);
|
var test = GetTestById(testId);
|
||||||
CloudEvent expected = SampleEvents.FromId(test.SampleId);
|
CloudEvent expected = SampleEvents.FromId(test.SampleId);
|
||||||
var extensions = test.SampleExtensionAttributes ? SampleEvents.SampleExtensionAttributes : null;
|
var extensions = test.SampleExtensionAttributes ? SampleEvents.SampleExtensionAttributes : null;
|
||||||
CloudEvent actual = new JsonEventFormatter().ConvertFromJsonElement(test.Event, extensions);
|
CloudEvent actual = (useContext ? new JsonEventFormatter(GeneratedJsonContext.Default) : new JsonEventFormatter())
|
||||||
|
.ConvertFromJsonElement(test.Event, extensions);
|
||||||
TestHelpers.AssertCloudEventsEqual(expected, actual, TestHelpers.InstantOnlyTimestampComparer);
|
TestHelpers.AssertCloudEventsEqual(expected, actual, TestHelpers.InstantOnlyTimestampComparer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, MemberData(nameof(InvalidEventTestIds))]
|
[Theory, MemberData(nameof(InvalidEventTestIds))]
|
||||||
public void InvalidEvent(string testId)
|
public void InvalidEvent(string testId, bool useContext)
|
||||||
{
|
{
|
||||||
var test = GetTestById(testId);
|
var test = GetTestById(testId);
|
||||||
var formatter = new JsonEventFormatter();
|
var formatter = useContext ? new JsonEventFormatter(GeneratedJsonContext.Default) : new JsonEventFormatter();
|
||||||
var extensions = test.SampleExtensionAttributes ? SampleEvents.SampleExtensionAttributes : null;
|
var extensions = test.SampleExtensionAttributes ? SampleEvents.SampleExtensionAttributes : null;
|
||||||
// Hmm... we throw FormatException in some cases, when ArgumentException would be better.
|
// Hmm... we throw FormatException in some cases, when ArgumentException would be better.
|
||||||
// Changing that would be "somewhat breaking"... it's unclear how much we should worry.
|
// Changing that would be "somewhat breaking"... it's unclear how much we should worry.
|
||||||
|
@ -50,7 +53,7 @@ public class ConformanceTest
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, MemberData(nameof(ValidBatchTestIds))]
|
[Theory, MemberData(nameof(ValidBatchTestIds))]
|
||||||
public void ValidBatch(string testId)
|
public void ValidBatch(string testId, bool useContext)
|
||||||
{
|
{
|
||||||
var test = GetTestById(testId);
|
var test = GetTestById(testId);
|
||||||
IReadOnlyList<CloudEvent> expected = SampleBatches.FromId(test.SampleId);
|
IReadOnlyList<CloudEvent> expected = SampleBatches.FromId(test.SampleId);
|
||||||
|
@ -58,15 +61,16 @@ public class ConformanceTest
|
||||||
// We don't have a convenience method for batches, so serialize the array back to JSON.
|
// We don't have a convenience method for batches, so serialize the array back to JSON.
|
||||||
var json = test.Batch.ToString();
|
var json = test.Batch.ToString();
|
||||||
var body = Encoding.UTF8.GetBytes(json);
|
var body = Encoding.UTF8.GetBytes(json);
|
||||||
IReadOnlyList<CloudEvent> actual = new JsonEventFormatter().DecodeBatchModeMessage(body, contentType: null, extensions);
|
IReadOnlyList<CloudEvent> actual = (useContext ? new JsonEventFormatter(GeneratedJsonContext.Default) : new JsonEventFormatter())
|
||||||
|
.DecodeBatchModeMessage(body, contentType: null, extensions);
|
||||||
TestHelpers.AssertBatchesEqual(expected, actual, TestHelpers.InstantOnlyTimestampComparer);
|
TestHelpers.AssertBatchesEqual(expected, actual, TestHelpers.InstantOnlyTimestampComparer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, MemberData(nameof(InvalidBatchTestIds))]
|
[Theory, MemberData(nameof(InvalidBatchTestIds))]
|
||||||
public void InvalidBatch(string testId)
|
public void InvalidBatch(string testId, bool useContext)
|
||||||
{
|
{
|
||||||
var test = GetTestById(testId);
|
var test = GetTestById(testId);
|
||||||
var formatter = new JsonEventFormatter();
|
var formatter = useContext ? new JsonEventFormatter(GeneratedJsonContext.Default) : new JsonEventFormatter();
|
||||||
var extensions = test.SampleExtensionAttributes ? SampleEvents.SampleExtensionAttributes : null;
|
var extensions = test.SampleExtensionAttributes ? SampleEvents.SampleExtensionAttributes : null;
|
||||||
// We don't have a convenience method for batches, so serialize the array back to JSON.
|
// We don't have a convenience method for batches, so serialize the array back to JSON.
|
||||||
var json = test.Batch.ToString();
|
var json = test.Batch.ToString();
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2023 Cloud Native Foundation.
|
||||||
|
// Licensed under the Apache 2.0 license.
|
||||||
|
// See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
namespace CloudNative.CloudEvents.SystemTextJson.UnitTests;
|
||||||
|
|
||||||
|
[System.Text.Json.Serialization.JsonSerializable(typeof(JsonConformanceTest))]
|
||||||
|
[System.Text.Json.Serialization.JsonSerializable(typeof(AttributedModel))]
|
||||||
|
[System.Text.Json.Serialization.JsonSerializable(typeof(int))]
|
||||||
|
internal partial class GeneratedJsonContext : System.Text.Json.Serialization.JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -14,5 +14,6 @@
|
||||||
|
|
||||||
<!-- Never pack any test projects -->
|
<!-- Never pack any test projects -->
|
||||||
<IsPackable>False</IsPackable>
|
<IsPackable>False</IsPackable>
|
||||||
|
<LangVersion>12.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in New Issue