Skip to content

Commit 6b24309

Browse files
committed
Add .NET 7, 8, trimming and AOT support
Also fixed several nullability warnings. Signed-off-by: Giovanni Bassi <[email protected]>
1 parent 6f48433 commit 6b24309

36 files changed

+822
-264
lines changed

.github/workflows/build.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: Build
22

33
on:
4+
workflow_dispatch:
45
push:
56
branches:
67
- main
@@ -15,15 +16,15 @@ jobs:
1516

1617
steps:
1718
- name: Check out our repo
18-
uses: actions/checkout@v3
19+
uses: actions/checkout@v4
1920
with:
2021
submodules: true
2122

2223
# Build with .NET 6.0 SDK
23-
- name: Setup .NET 6.0
24+
- name: Setup .NET 8.0
2425
uses: actions/setup-dotnet@v3
2526
with:
26-
dotnet-version: 6.0.x
27+
dotnet-version: 8.0.x
2728

2829
- name: Build
2930
run: |

.github/workflows/nuget.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ jobs:
1313

1414
steps:
1515
- name: Check out our repo
16-
uses: actions/checkout@v3
16+
uses: actions/checkout@v4
1717
with:
1818
submodules: true
1919

2020
# Build with .NET 6.0 SDK
2121
- name: Setup .NET 6.0
2222
uses: actions/setup-dotnet@v3
2323
with:
24-
dotnet-version: 6.0.x
24+
dotnet-version: 8.0.x
2525

2626
- name: Build
2727
run: |

CloudEvents.sln

+34
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "xml", "xml", "{4012C753-68D
7272
conformance\format\xml\valid-events.xml = conformance\format\xml\valid-events.xml
7373
EndProjectSection
7474
EndProject
75+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpSendJson", "samples\HttpSendJson\HttpSendJson.csproj", "{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}"
76+
EndProject
77+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}"
78+
EndProject
79+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudNative.CloudEvents.MinApiSample", "samples\CloudNative.CloudEvents.MinApiSample\CloudNative.CloudEvents.MinApiSample.csproj", "{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}"
80+
EndProject
7581
Global
7682
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7783
Debug|Any CPU = Debug|Any CPU
@@ -238,15 +244,43 @@ Global
238244
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x64.Build.0 = Release|Any CPU
239245
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x86.ActiveCfg = Release|Any CPU
240246
{9D82AC2B-0075-4161-AE0E-4A6629C9FF2A}.Release|x86.Build.0 = Release|Any CPU
247+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
248+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
249+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x64.ActiveCfg = Debug|Any CPU
250+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x64.Build.0 = Debug|Any CPU
251+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x86.ActiveCfg = Debug|Any CPU
252+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Debug|x86.Build.0 = Debug|Any CPU
253+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
254+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|Any CPU.Build.0 = Release|Any CPU
255+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x64.ActiveCfg = Release|Any CPU
256+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x64.Build.0 = Release|Any CPU
257+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x86.ActiveCfg = Release|Any CPU
258+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8}.Release|x86.Build.0 = Release|Any CPU
259+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
260+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
261+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x64.ActiveCfg = Debug|Any CPU
262+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x64.Build.0 = Debug|Any CPU
263+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x86.ActiveCfg = Debug|Any CPU
264+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Debug|x86.Build.0 = Debug|Any CPU
265+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
266+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|Any CPU.Build.0 = Release|Any CPU
267+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x64.ActiveCfg = Release|Any CPU
268+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x64.Build.0 = Release|Any CPU
269+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x86.ActiveCfg = Release|Any CPU
270+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF}.Release|x86.Build.0 = Release|Any CPU
241271
EndGlobalSection
242272
GlobalSection(SolutionProperties) = preSolution
243273
HideSolutionNode = FALSE
244274
EndGlobalSection
245275
GlobalSection(NestedProjects) = preSolution
276+
{F1B9B769-DB6B-481F-905C-24FE3B12E00E} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
277+
{9760D744-D1BF-40E3-BD6F-7F639BFB9188} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
246278
{A5906FBA-D73A-4A09-8539-CB10D7B586AE} = {8CCC98B3-1776-49FF-96D6-947A9E5DFB0A}
247279
{D8055631-E6BB-4CD2-8162-F674D6D30E76} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
248280
{119AD438-878B-4383-BC9F-779F1605E711} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
249281
{4012C753-68DE-4737-936F-F5DBC485C51B} = {A5906FBA-D73A-4A09-8539-CB10D7B586AE}
282+
{730D4C5E-DC5B-498C-ADFB-05CB81ECCEC8} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
283+
{1566A665-9FFF-4D87-9C7B-CC06C72C9BFF} = {5AD5E051-9A8E-46D9-B0C5-8933718C6D1F}
250284
EndGlobalSection
251285
GlobalSection(ExtensibilityGlobals) = postSolution
252286
SolutionGuid = {F77A454C-CC17-4AD6-823A-64E1A94FDA0A}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<Nullable>enable</Nullable>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<ProjectReference Include="..\..\src\CloudNative.CloudEvents.AspNetCore\CloudNative.CloudEvents.AspNetCore.csproj" />
9+
<ProjectReference Include="..\..\src\CloudNative.CloudEvents\CloudNative.CloudEvents.csproj" />
10+
<ProjectReference Include="..\..\src\CloudNative.CloudEvents.SystemTextJson\CloudNative.CloudEvents.SystemTextJson.csproj" />
11+
</ItemGroup>
12+
<PropertyGroup Condition="'$(Configuration)'=='Release'">
13+
<SelfContained>true</SelfContained>
14+
<PublishAot>true</PublishAot>
15+
<DebugType>None</DebugType>
16+
<DebugSymbols>False</DebugSymbols>
17+
</PropertyGroup>
18+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Cloud Native Foundation.
2+
// Licensed under the Apache 2.0 license.
3+
// See LICENSE file in the project root for full license information.
4+
5+
using CloudNative.CloudEvents;
6+
using CloudNative.CloudEvents.Http;
7+
using CloudNative.CloudEvents.SystemTextJson;
8+
using CloudNative.CloudEvents.AspNetCore;
9+
using System.Text.Json.Serialization;
10+
using System.Text.Json;
11+
using System.Text;
12+
13+
var builder = WebApplication.CreateBuilder(args);
14+
var app = builder.Build();
15+
var formatter = new JsonEventFormatter<Message>(MyJsonContext.Default);
16+
17+
app.MapPost("/api/events/receive/", async (HttpRequest request) =>
18+
{
19+
var cloudEvent = await request.ToCloudEventAsync(formatter);
20+
using var ms = new MemoryStream();
21+
using var writer = new Utf8JsonWriter(ms, new() { Indented = true });
22+
writer.WriteStartObject();
23+
foreach (var (attribute, value) in cloudEvent.GetPopulatedAttributes())
24+
writer.WriteString(attribute.Name, attribute.Format(value));
25+
writer.WriteEndObject();
26+
await writer.FlushAsync();
27+
var attributeMap = Encoding.UTF8.GetString(ms.ToArray());
28+
return Results.Text($"Received event with ID {cloudEvent.Id}, attributes: {attributeMap}");
29+
});
30+
31+
app.MapPost("/api/events/receive2/", (Event e) => Results.Json(e.CloudEvent.Data, MyJsonContext.Default));
32+
33+
app.MapPost("/api/events/receive3/", (Message message) => Results.Json(message, MyJsonContext.Default));
34+
35+
app.MapGet("/api/events/generate/", () =>
36+
{
37+
var evt = new CloudEvent
38+
{
39+
Type = "CloudNative.CloudEvents.MinApiSample",
40+
Source = new Uri("https://github.com/cloudevents/sdk-csharp"),
41+
Time = DateTimeOffset.Now,
42+
DataContentType = "application/json",
43+
Id = Guid.NewGuid().ToString(),
44+
Data = new Message("C#", Environment.Version.ToString())
45+
};
46+
// Format the event as the body of the response. This is UTF-8 JSON because of
47+
// the CloudEventFormatter we're using, but EncodeStructuredModeMessage always
48+
// returns binary data. We could return the data directly, but for debugging
49+
// purposes it's useful to have the JSON string.
50+
var bytes = formatter.EncodeStructuredModeMessage(evt, out var contentType);
51+
string json = Encoding.UTF8.GetString(bytes.Span);
52+
// Specify the content type of the response: this is what makes it a CloudEvent.
53+
// (In "binary mode", the content type is the content type of the data, and headers
54+
// indicate that it's a CloudEvent.)
55+
return Results.Content(json, contentType.MediaType, Encoding.UTF8);
56+
});
57+
58+
app.Run();
59+
60+
[JsonSerializable(typeof(Message))]
61+
internal partial class MyJsonContext : JsonSerializerContext { }
62+
63+
public class Event
64+
{
65+
private readonly static JsonEventFormatter formatter = new JsonEventFormatter<Message>(MyJsonContext.Default);
66+
// required for receive2
67+
public static async ValueTask<Event?> BindAsync(HttpContext context)
68+
{
69+
var cloudEvent = await context.Request.ToCloudEventAsync(formatter);
70+
return new Event { CloudEvent = cloudEvent };
71+
}
72+
public required CloudEvent CloudEvent { get; init; }
73+
}
74+
75+
record class Message(string Language, string EnvironmentVersion)
76+
{
77+
private readonly static JsonEventFormatter formatter = new JsonEventFormatter<Message>(MyJsonContext.Default);
78+
// required for receive3
79+
public static async ValueTask<Message?> BindAsync(HttpContext context)
80+
{
81+
var cloudEvent = await context.Request.ToCloudEventAsync(formatter);
82+
return cloudEvent.Data is Message message ? message : null;
83+
}
84+
}
85+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"profiles": {
3+
"http": {
4+
"commandName": "Project",
5+
"dotnetRunMessages": true,
6+
"launchBrowser": true,
7+
"launchUrl": "api/events/generate",
8+
"applicationUrl": "http://localhost:5002",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
}
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}

samples/Directory.Build.props

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414

1515
<!-- Never pack any sample projects -->
1616
<IsPackable>False</IsPackable>
17+
<LangVersion>12.0</LangVersion>
1718
</PropertyGroup>
1819
</Project>
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<PackageReference Include="docopt.net" Version="0.8.1" />
10+
<ProjectReference Include="..\..\src\CloudNative.CloudEvents\CloudNative.CloudEvents.csproj" />
11+
<ProjectReference Include="..\..\src\CloudNative.CloudEvents.SystemTextJson\CloudNative.CloudEvents.SystemTextJson.csproj" />
12+
</ItemGroup>
13+
<PropertyGroup Condition="'$(Configuration)'=='Release'">
14+
<SelfContained>true</SelfContained>
15+
<PublishAot>true</PublishAot>
16+
<DebugType>None</DebugType>
17+
<DebugSymbols>False</DebugSymbols>
18+
</PropertyGroup>
19+
</Project>

samples/HttpSendJson/Program.cs

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) Cloud Native Foundation.
2+
// Licensed under the Apache 2.0 license.
3+
// See LICENSE file in the project root for full license information.
4+
5+
using CloudNative.CloudEvents;
6+
using CloudNative.CloudEvents.Http;
7+
using CloudNative.CloudEvents.SystemTextJson;
8+
using DocoptNet;
9+
using System.Net.Mime;
10+
using static System.Console;
11+
12+
// This application uses the docopt.net library for parsing the command
13+
// line and calling the application code.
14+
ProgramArguments programArguments = new();
15+
var result = await ProgramArguments.CreateParserWithVersion()
16+
.Parse(args)
17+
.Match(RunAsync,
18+
result => { WriteLine(result.Help); return Task.FromResult(1); },
19+
result => { WriteLine(result.Version); return Task.FromResult(0); },
20+
result => { Error.WriteLine(result.Usage); return Task.FromResult(1); });
21+
return result;
22+
23+
static async Task<int> RunAsync(ProgramArguments args)
24+
{
25+
var cloudEvent = new CloudEvent
26+
{
27+
Id = Guid.NewGuid().ToString(),
28+
Type = args.OptType,
29+
Source = new Uri(args.OptSource),
30+
DataContentType = MediaTypeNames.Application.Json,
31+
Data = System.Text.Json.JsonSerializer.Serialize("hey there!", GeneratedJsonContext.Default.String)
32+
};
33+
34+
var content = cloudEvent.ToHttpContent(ContentMode.Structured, new JsonEventFormatter(GeneratedJsonContext.Default));
35+
36+
var httpClient = new HttpClient();
37+
// Your application remains in charge of adding any further headers or
38+
// other information required to authenticate/authorize or otherwise
39+
// dispatch the call at the server.
40+
var result = await httpClient.PostAsync(args.OptUrl, content);
41+
42+
WriteLine(result.StatusCode);
43+
return 0;
44+
}
45+
46+
[System.Text.Json.Serialization.JsonSerializable(typeof(string))]
47+
internal partial class GeneratedJsonContext : System.Text.Json.Serialization.JsonSerializerContext
48+
{
49+
}
50+
51+
[DocoptArguments]
52+
partial class ProgramArguments
53+
{
54+
const string Help = @"HttpSendJson.
55+
56+
Usage:
57+
HttpSendJson --url=URL [--type=TYPE] [--source=SOURCE]
58+
HttpSendJson (-h | --help)
59+
HttpSendJson --version
60+
61+
Options:
62+
--url=URL HTTP(S) address to send the event to.
63+
--type=TYPE CloudEvents 'type' [default: com.example.myevent].
64+
--source=SOURCE CloudEvents 'source' [default: urn:example-com:mysource:abc].
65+
-h --help Show this screen.
66+
--version Show version.
67+
";
68+
public static string Version => $"producer {typeof(ProgramArguments).Assembly.GetName().Version}";
69+
public static IParser<ProgramArguments> CreateParserWithVersion() => CreateParser().WithVersion(Version);
70+
}

src/CloudNative.CloudEvents.Amqp/AmqpExtensions.cs

+22-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using CloudNative.CloudEvents.Core;
99
using System;
1010
using System.Collections.Generic;
11+
using System.Diagnostics.CodeAnalysis;
1112
using System.IO;
1213
using System.Net.Mime;
1314

@@ -145,7 +146,7 @@ public static CloudEvent ToCloudEvent(
145146
}
146147
}
147148

148-
private static bool HasCloudEventsContentType(Message message, out string? contentType)
149+
private static bool HasCloudEventsContentType(Message message, [NotNullWhen(true)] out string? contentType)
149150
{
150151
contentType = message.Properties.ContentType?.ToString();
151152
return MimeUtilities.IsCloudEventsContentType(contentType);
@@ -249,4 +250,23 @@ private static ApplicationProperties MapHeaders(CloudEvent cloudEvent, string pr
249250
return applicationProperties;
250251
}
251252
}
252-
}
253+
}
254+
255+
#if NETSTANDARD2_0
256+
namespace System.Diagnostics.CodeAnalysis
257+
{
258+
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
259+
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
260+
internal sealed class NotNullWhenAttribute : Attribute
261+
{
262+
/// <summary>Initializes the attribute with the specified return value condition.</summary>
263+
/// <param name="returnValue">
264+
/// The return value condition. If the method returns this value, the associated parameter will not be null.
265+
/// </param>
266+
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
267+
268+
/// <summary>Gets the return value condition.</summary>
269+
public bool ReturnValue { get; }
270+
}
271+
}
272+
#endif

src/CloudNative.CloudEvents.Amqp/CloudNative.CloudEvents.Amqp.csproj

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
55
<Description>AMQP extensions for CloudNative.CloudEvents</Description>
6-
<LangVersion>8.0</LangVersion>
76
<Nullable>enable</Nullable>
87
<PackageTags>cncf;cloudnative;cloudevents;events;amqp</PackageTags>
98
</PropertyGroup>

0 commit comments

Comments
 (0)