diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a5afda..fb64d7b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,11 +19,14 @@ jobs: with: submodules: true - # Build with .NET 6.0 SDK - - name: Setup .NET 6.0 + # Build with .NET 8.0 SDK + # Test with .NET 6.0 and 8.0 + - name: Setup .NET 6.0 and 8.0 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: | + 8.0.x + 6.0.x - name: Build run: | diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 4ee48a5..a5e8400 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -17,11 +17,14 @@ jobs: with: submodules: true - # Build with .NET 6.0 SDK - - name: Setup .NET 6.0 + # Build with .NET 8.0 SDK + # Test with .NET 6.0 and 8.0 + - name: Setup .NET 6.0 and 8.0 uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: | + 8.0.x + 6.0.x - name: Build run: | diff --git a/global.json b/global.json new file mode 100644 index 0000000..bb1fa42 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.100", + "allowPrerelease": false, + "rollForward": "latestMinor" + } +} \ No newline at end of file diff --git a/src/CloudNative.CloudEvents.Amqp/AmqpExtensions.cs b/src/CloudNative.CloudEvents.Amqp/AmqpExtensions.cs index 001b832..591e0af 100644 --- a/src/CloudNative.CloudEvents.Amqp/AmqpExtensions.cs +++ b/src/CloudNative.CloudEvents.Amqp/AmqpExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Cloud Native Foundation. +// Copyright (c) Cloud Native Foundation. // Licensed under the Apache 2.0 license. // See LICENSE file in the project root for full license information. @@ -8,6 +8,7 @@ using CloudNative.CloudEvents.Core; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Mime; @@ -145,7 +146,7 @@ public static CloudEvent ToCloudEvent( } } - 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(); return MimeUtilities.IsCloudEventsContentType(contentType); @@ -249,4 +250,4 @@ private static ApplicationProperties MapHeaders(CloudEvent cloudEvent, string pr return applicationProperties; } } -} \ No newline at end of file +} diff --git a/src/CloudNative.CloudEvents.Amqp/CloudNative.CloudEvents.Amqp.csproj b/src/CloudNative.CloudEvents.Amqp/CloudNative.CloudEvents.Amqp.csproj index a0506ec..2680861 100644 --- a/src/CloudNative.CloudEvents.Amqp/CloudNative.CloudEvents.Amqp.csproj +++ b/src/CloudNative.CloudEvents.Amqp/CloudNative.CloudEvents.Amqp.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 AMQP extensions for CloudNative.CloudEvents 8.0 enable @@ -12,6 +12,8 @@ + + diff --git a/src/CloudNative.CloudEvents.AspNetCore/CloudNative.CloudEvents.AspNetCore.csproj b/src/CloudNative.CloudEvents.AspNetCore/CloudNative.CloudEvents.AspNetCore.csproj index b073a81..401cee4 100644 --- a/src/CloudNative.CloudEvents.AspNetCore/CloudNative.CloudEvents.AspNetCore.csproj +++ b/src/CloudNative.CloudEvents.AspNetCore/CloudNative.CloudEvents.AspNetCore.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 ASP.Net Core extensions for CloudNative.CloudEvents 8.0 enable diff --git a/src/CloudNative.CloudEvents.Avro/AvroEventFormatter.cs b/src/CloudNative.CloudEvents.Avro/AvroEventFormatter.cs index 8360d8b..021a5ed 100644 --- a/src/CloudNative.CloudEvents.Avro/AvroEventFormatter.cs +++ b/src/CloudNative.CloudEvents.Avro/AvroEventFormatter.cs @@ -185,9 +185,9 @@ private static RecordSchema ParseEmbeddedSchema() // will fail and that's okay since the type is useless without the proper schema. using var sr = new StreamReader(typeof(AvroEventFormatter) .Assembly - .GetManifestResourceStream("CloudNative.CloudEvents.Avro.AvroSchema.json")); + .GetManifestResourceStream("CloudNative.CloudEvents.Avro.AvroSchema.json")!); return (RecordSchema) Schema.Parse(sr.ReadToEnd()); } } -} \ No newline at end of file +} diff --git a/src/CloudNative.CloudEvents.Avro/CloudNative.CloudEvents.Avro.csproj b/src/CloudNative.CloudEvents.Avro/CloudNative.CloudEvents.Avro.csproj index 3f0fb6e..5534740 100644 --- a/src/CloudNative.CloudEvents.Avro/CloudNative.CloudEvents.Avro.csproj +++ b/src/CloudNative.CloudEvents.Avro/CloudNative.CloudEvents.Avro.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 Avro extensions for CloudNative.CloudEvents cncf;cloudnative;cloudevents;events;avro 10.0 diff --git a/src/CloudNative.CloudEvents.Kafka/CloudNative.CloudEvents.Kafka.csproj b/src/CloudNative.CloudEvents.Kafka/CloudNative.CloudEvents.Kafka.csproj index 9926330..1971882 100644 --- a/src/CloudNative.CloudEvents.Kafka/CloudNative.CloudEvents.Kafka.csproj +++ b/src/CloudNative.CloudEvents.Kafka/CloudNative.CloudEvents.Kafka.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 Kafka extensions for CloudNative.CloudEvents cncf;cloudnative;cloudevents;events;kafka 8.0 diff --git a/src/CloudNative.CloudEvents.Mqtt/CloudNative.CloudEvents.Mqtt.csproj b/src/CloudNative.CloudEvents.Mqtt/CloudNative.CloudEvents.Mqtt.csproj index 58f622e..5638bd3 100644 --- a/src/CloudNative.CloudEvents.Mqtt/CloudNative.CloudEvents.Mqtt.csproj +++ b/src/CloudNative.CloudEvents.Mqtt/CloudNative.CloudEvents.Mqtt.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 MQTT extensions for CloudNative.CloudEvents cncf;cloudnative;cloudevents;events;mqtt 8.0 diff --git a/src/CloudNative.CloudEvents.NewtonsoftJson/CloudNative.CloudEvents.NewtonsoftJson.csproj b/src/CloudNative.CloudEvents.NewtonsoftJson/CloudNative.CloudEvents.NewtonsoftJson.csproj index 3ee42e3..6bd7a69 100644 --- a/src/CloudNative.CloudEvents.NewtonsoftJson/CloudNative.CloudEvents.NewtonsoftJson.csproj +++ b/src/CloudNative.CloudEvents.NewtonsoftJson/CloudNative.CloudEvents.NewtonsoftJson.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 JSON support for the CNCF CloudEvents SDK, based on Newtonsoft.Json. 8.0 enable diff --git a/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs b/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs index 20c9150..3564135 100644 --- a/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs +++ b/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Cloud Native Foundation. +// Copyright (c) Cloud Native Foundation. // Licensed under the Apache 2.0 license. // See LICENSE file in the project root for full license information. @@ -345,7 +345,7 @@ protected virtual void DecodeStructuredModeDataBase64Property(JToken dataBase64T { throw new ArgumentException($"Structured mode property '{DataBase64PropertyName}' must be a string, when present."); } - cloudEvent.Data = Convert.FromBase64String((string?) dataBase64Token); + cloudEvent.Data = Convert.FromBase64String((string) dataBase64Token!); } /// @@ -498,7 +498,7 @@ private void WriteCloudEventForBatchOrStructuredMode(JsonWriter writer, CloudEve /// /// The CloudEvent to infer the data content from. Must not be null. /// The inferred data content type, or null if no inference is performed. - protected override string? InferDataContentType(object data) => data is byte[]? null : JsonMediaType; + protected override string? InferDataContentType(object data) => data is byte[] ? null : JsonMediaType; /// /// Encodes structured mode data within a CloudEvent, writing it to the specified . @@ -524,7 +524,14 @@ protected virtual void EncodeStructuredModeData(CloudEvent cloudEvent, JsonWrite } else { - ContentType dataContentType = new ContentType(GetOrInferDataContentType(cloudEvent)); + string? dataContentTypeText = GetOrInferDataContentType(cloudEvent); + // This would only happen in a derived class which overrides GetOrInferDataContentType further... + // This class infers application/json for anything other than byte arrays. + if (dataContentTypeText is null) + { + throw new ArgumentException("Data content type cannot be inferred"); + } + ContentType dataContentType = new ContentType(dataContentTypeText); if (IsJsonMediaType(dataContentType.MediaType)) { writer.WritePropertyName(DataPropertyName); @@ -710,4 +717,4 @@ protected override void DecodeStructuredModeDataProperty(JToken dataToken, Cloud protected override void DecodeStructuredModeDataBase64Property(JToken dataBase64Token, CloudEvent cloudEvent) => throw new ArgumentException($"Data unexpectedly represented using '{DataBase64PropertyName}' within structured mode CloudEvent."); } -} \ No newline at end of file +} diff --git a/src/CloudNative.CloudEvents.Protobuf/CloudNative.CloudEvents.Protobuf.csproj b/src/CloudNative.CloudEvents.Protobuf/CloudNative.CloudEvents.Protobuf.csproj index c21c60e..cbee993 100644 --- a/src/CloudNative.CloudEvents.Protobuf/CloudNative.CloudEvents.Protobuf.csproj +++ b/src/CloudNative.CloudEvents.Protobuf/CloudNative.CloudEvents.Protobuf.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 Support for the Protobuf event format in for CloudNative.CloudEvents cncf;cloudnative;cloudevents;events;protobuf 10.0 diff --git a/src/CloudNative.CloudEvents.SystemTextJson/CloudNative.CloudEvents.SystemTextJson.csproj b/src/CloudNative.CloudEvents.SystemTextJson/CloudNative.CloudEvents.SystemTextJson.csproj index d5c0916..592d282 100644 --- a/src/CloudNative.CloudEvents.SystemTextJson/CloudNative.CloudEvents.SystemTextJson.csproj +++ b/src/CloudNative.CloudEvents.SystemTextJson/CloudNative.CloudEvents.SystemTextJson.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 JSON support for the CNCF CloudEvents SDK, based on System.Text.Json. 8.0 cncf;cloudnative;cloudevents;events;json;systemtextjson diff --git a/src/CloudNative.CloudEvents/CloudEventFormatterAttribute.cs b/src/CloudNative.CloudEvents/CloudEventFormatterAttribute.cs index 689abd8..00fd06c 100644 --- a/src/CloudNative.CloudEvents/CloudEventFormatterAttribute.cs +++ b/src/CloudNative.CloudEvents/CloudEventFormatterAttribute.cs @@ -56,7 +56,7 @@ public CloudEventFormatterAttribute(Type formatterType) => throw new ArgumentException($"The {nameof(CloudEventFormatterAttribute)} on type {targetType} has no converter type specified.", nameof(targetType)); } - object instance; + object? instance; try { instance = Activator.CreateInstance(formatterType); diff --git a/src/CloudNative.CloudEvents/CloudNative.CloudEvents.csproj b/src/CloudNative.CloudEvents/CloudNative.CloudEvents.csproj index 3561218..250a26c 100644 --- a/src/CloudNative.CloudEvents/CloudNative.CloudEvents.csproj +++ b/src/CloudNative.CloudEvents/CloudNative.CloudEvents.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 CNCF CloudEvents SDK latest enable @@ -9,7 +9,7 @@ - + diff --git a/src/CloudNative.CloudEvents/Core/BinaryDataUtilities.cs b/src/CloudNative.CloudEvents/Core/BinaryDataUtilities.cs index 29dfa1d..c1915a9 100644 --- a/src/CloudNative.CloudEvents/Core/BinaryDataUtilities.cs +++ b/src/CloudNative.CloudEvents/Core/BinaryDataUtilities.cs @@ -65,7 +65,7 @@ public static ReadOnlyMemory ToReadOnlyMemory(Stream stream) public static MemoryStream AsStream(ReadOnlyMemory 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); } /// @@ -79,7 +79,7 @@ public static string GetString(ReadOnlyMemory memory, Encoding encoding) // TODO: If we introduce an additional netstandard2.1 target, we can use encoding.GetString(memory.Span) var segment = GetArraySegment(memory); - return encoding.GetString(segment.Array, segment.Offset, segment.Count); + return encoding.GetString(segment.Array!, segment.Offset, segment.Count); } /// @@ -92,7 +92,7 @@ public static async Task CopyToStreamAsync(ReadOnlyMemory source, Stream d { Validation.CheckNotNull(destination, nameof(destination)); 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); } /// @@ -108,13 +108,14 @@ public static byte[] AsArray(ReadOnlyMemory memory) var segment = GetArraySegment(memory); // 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. - return segment.Offset == 0 && segment.Count == segment.Array.Length + return segment.Offset == 0 && segment.Count == segment.Array!.Length ? segment.Array : memory.ToArray(); } + // Note: when this returns, the Array property of the returned segment is guaranteed not to be null. private static ArraySegment GetArraySegment(ReadOnlyMemory memory) => - MemoryMarshal.TryGetArray(memory, out var segment) + MemoryMarshal.TryGetArray(memory, out var segment) && segment.Array is not null ? segment : new ArraySegment(memory.ToArray()); } diff --git a/src/CloudNative.CloudEvents/Core/MimeUtilities.cs b/src/CloudNative.CloudEvents/Core/MimeUtilities.cs index b660086..d31d776 100644 --- a/src/CloudNative.CloudEvents/Core/MimeUtilities.cs +++ b/src/CloudNative.CloudEvents/Core/MimeUtilities.cs @@ -1,8 +1,9 @@ -// Copyright 2021 Cloud Native Foundation. +// Copyright 2021 Cloud Native Foundation. // Licensed under the Apache 2.0 license. // See LICENSE file in the project root for full license information. using System; +using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Net.Mime; using System.Text; @@ -57,7 +58,7 @@ public static Encoding GetEncoding(ContentType? contentType) => var header = new MediaTypeHeaderValue(contentType.MediaType); 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])); } return header; } @@ -76,7 +77,7 @@ public static Encoding GetEncoding(ContentType? contentType) => /// /// The content type to check. May be null, in which case the result is false. /// true if the given content type denotes a (non-batch) CloudEvent; false otherwise - public static bool IsCloudEventsContentType(string? contentType) => + public static bool IsCloudEventsContentType([NotNullWhen(true)] string? contentType) => contentType is string && contentType.StartsWith(MediaType, StringComparison.InvariantCultureIgnoreCase) && !contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase); @@ -86,7 +87,7 @@ contentType is string && /// /// The content type to check. May be null, in which case the result is false. /// true if the given content type represents a CloudEvent batch; false otherwise - public static bool IsCloudEventsBatchContentType(string? contentType) => + public static bool IsCloudEventsBatchContentType([NotNullWhen(true)] string? contentType) => contentType is string && contentType.StartsWith(BatchMediaType, StringComparison.InvariantCultureIgnoreCase); } } diff --git a/src/CloudNative.CloudEvents/Http/HttpClientExtensions.cs b/src/CloudNative.CloudEvents/Http/HttpClientExtensions.cs index b439ffc..2ac175c 100644 --- a/src/CloudNative.CloudEvents/Http/HttpClientExtensions.cs +++ b/src/CloudNative.CloudEvents/Http/HttpClientExtensions.cs @@ -1,10 +1,11 @@ -// Copyright (c) Cloud Native Foundation. +// 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.Core; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -130,7 +131,7 @@ public static Task ToCloudEventAsync( return ToCloudEventInternalAsync(httpRequestMessage.Headers, httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage)); } - private static async Task ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content, + private static async Task ToCloudEventInternalAsync(HttpHeaders headers, HttpContent? content, CloudEventFormatter formatter, IEnumerable? extensionAttributes, string paramName) { Validation.CheckNotNull(formatter, nameof(formatter)); @@ -142,7 +143,7 @@ private static async Task ToCloudEventInternalAsync(HttpHeaders head } else { - string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content.Headers); + string? versionId = MaybeGetVersionId(headers) ?? MaybeGetVersionId(content?.Headers); 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)); @@ -151,7 +152,8 @@ private static async Task ToCloudEventInternalAsync(HttpHeaders head ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName); var cloudEvent = new CloudEvent(version, extensionAttributes); - foreach (var header in headers.Concat(content.Headers)) + var allHeaders = content is null ? headers : headers.Concat(content.Headers); + foreach (var header in allHeaders) { string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key); if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) @@ -231,7 +233,7 @@ public static Task> ToCloudEventBatchAsync( return ToCloudEventBatchInternalAsync(httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage)); } - private static async Task> ToCloudEventBatchInternalAsync(HttpContent content, + private static async Task> ToCloudEventBatchInternalAsync(HttpContent? content, CloudEventFormatter formatter, IEnumerable? extensionAttributes, string paramName) { Validation.CheckNotNull(formatter, nameof(formatter)); @@ -331,16 +333,16 @@ public static HttpContent ToHttpContent(this IReadOnlyList cloudEven } private static ByteArrayContent ToByteArrayContent(ReadOnlyMemory content) => - MemoryMarshal.TryGetArray(content, out var segment) + MemoryMarshal.TryGetArray(content, out var segment) && segment.Array is not null ? new ByteArrayContent(segment.Array, segment.Offset, segment.Count) // TODO: Just throw? : new ByteArrayContent(content.ToArray()); // 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); - private static bool HasCloudEventsBatchContentType(HttpContent content) => + private static bool HasCloudEventsBatchContentType([NotNullWhen(true)] HttpContent? content) => MimeUtilities.IsCloudEventsBatchContentType(content?.Headers?.ContentType?.MediaType); private static string? MaybeGetVersionId(HttpHeaders? headers) => @@ -348,4 +350,4 @@ private static bool HasCloudEventsBatchContentType(HttpContent content) => ? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First() : null; } -} \ No newline at end of file +} diff --git a/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs b/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs index 1fce6ba..0d830d6 100644 --- a/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs +++ b/src/CloudNative.CloudEvents/Http/HttpListenerExtensions.cs @@ -179,7 +179,7 @@ private static async Task ToCloudEventAsyncImpl(HttpListenerRequest } else { - string versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader]; + string? versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader]; 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)); @@ -191,12 +191,18 @@ private static async Task ToCloudEventAsyncImpl(HttpListenerRequest var headers = httpListenerRequest.Headers; foreach (var key in headers.AllKeys) { + // It would be highly unusual for either the key or the value to be null, but + // the contract claims it's possible. Skip it if so. + if (key is null || headers[key] is not string headerValue) + { + continue; + } string? attributeName = HttpUtilities.GetAttributeNameFromHeaderName(key); if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name) { continue; } - string attributeValue = HttpUtilities.DecodeHeaderValue(headers[key]); + string attributeValue = HttpUtilities.DecodeHeaderValue(headerValue); cloudEvent.SetAttributeFromString(attributeName, attributeValue); } diff --git a/test/CloudNative.CloudEvents.IntegrationTests/CloudNative.CloudEvents.IntegrationTests.csproj b/test/CloudNative.CloudEvents.IntegrationTests/CloudNative.CloudEvents.IntegrationTests.csproj index da33b50..4fe867f 100644 --- a/test/CloudNative.CloudEvents.IntegrationTests/CloudNative.CloudEvents.IntegrationTests.csproj +++ b/test/CloudNative.CloudEvents.IntegrationTests/CloudNative.CloudEvents.IntegrationTests.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0;net8.0 diff --git a/test/CloudNative.CloudEvents.UnitTests/CloudNative.CloudEvents.UnitTests.csproj b/test/CloudNative.CloudEvents.UnitTests/CloudNative.CloudEvents.UnitTests.csproj index 0fc8ce6..b8aa2d8 100644 --- a/test/CloudNative.CloudEvents.UnitTests/CloudNative.CloudEvents.UnitTests.csproj +++ b/test/CloudNative.CloudEvents.UnitTests/CloudNative.CloudEvents.UnitTests.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0;net8.0 enable