diff --git a/.bot/README.md b/.bot/README.md index f3aa97d..f7e13f0 100644 --- a/.bot/README.md +++ b/.bot/README.md @@ -1,10 +1,10 @@ -# .bot Workspace - -This folder is reserved for local-only AI working material such as: - -- brainstorm notes -- draft implementation plans -- design alternatives -- temporary agent state - -Keep this folder out of source control. Move only finalized, non-confidential guidance into `AGENTS.md`. +# .bot Workspace + +This folder is reserved for local-only AI working material such as: + +- brainstorm notes +- draft implementation plans +- design alternatives +- temporary agent state + +Keep this folder out of source control. Move only finalized, non-confidential guidance into `AGENTS.md`. diff --git a/.docfx/BuildDocfxImage.ps1 b/.docfx/BuildDocfxImage.ps1 index bbaa1d6..20935a4 100644 --- a/.docfx/BuildDocfxImage.ps1 +++ b/.docfx/BuildDocfxImage.ps1 @@ -1,4 +1,4 @@ -$version = minver -i -t v -v w -docfx metadata docfx.json -docker buildx build -t carter-docfx:$version --platform linux/arm64,linux/amd64 --load -f Dockerfile.docfx . -get-childItem -recurse -path api -include *.yml, .manifest | remove-item +$version = minver -i -t v -v w +docfx metadata docfx.json +docker buildx build -t carter-docfx:$version --platform linux/arm64,linux/amd64 --load -f Dockerfile.docfx . +get-childItem -recurse -path api -include *.yml, .manifest | remove-item diff --git a/.docfx/Dockerfile.docfx b/.docfx/Dockerfile.docfx index 57a6e0f..e29eda1 100644 --- a/.docfx/Dockerfile.docfx +++ b/.docfx/Dockerfile.docfx @@ -1,16 +1,16 @@ -ARG NGINX_VERSION=1.31-alpine - -FROM --platform=$BUILDPLATFORM dhi.io/nginx:${NGINX_VERSION} AS base -RUN rm -rf /usr/share/nginx/html/* - -FROM --platform=$BUILDPLATFORM codebeltnet/docfx:2 AS build - -ADD [".", "docfx"] - -RUN cd docfx; \ - docfx build - -FROM dhi.io/nginx:${NGINX_VERSION} AS final -WORKDIR /usr/share/nginx/html -COPY --from=build /build/docfx/wwwroot /usr/share/nginx/html -COPY nginx.conf /etc/nginx/conf.d/default.conf +ARG NGINX_VERSION=1.31-alpine + +FROM --platform=$BUILDPLATFORM dhi.io/nginx:${NGINX_VERSION} AS base +RUN rm -rf /usr/share/nginx/html/* + +FROM --platform=$BUILDPLATFORM codebeltnet/docfx:2 AS build + +ADD [".", "docfx"] + +RUN cd docfx; \ + docfx build + +FROM dhi.io/nginx:${NGINX_VERSION} AS final +WORKDIR /usr/share/nginx/html +COPY --from=build /build/docfx/wwwroot /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/.docfx/PublishDocfxImage.ps1 b/.docfx/PublishDocfxImage.ps1 index 872b347..cbd8c6e 100644 --- a/.docfx/PublishDocfxImage.ps1 +++ b/.docfx/PublishDocfxImage.ps1 @@ -1,3 +1,3 @@ -$version = minver -i -t v -v w -docker tag carter-docfx:$version jcr.codebelt.net/geekle/carter-docfx:$version -docker push jcr.codebelt.net/geekle/carter-docfx:$version +$version = minver -i -t v -v w +docker tag carter-docfx:$version jcr.codebelt.net/geekle/carter-docfx:$version +docker push jcr.codebelt.net/geekle/carter-docfx:$version diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.md b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.md index 209efb3..c1e5e25 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.md @@ -1,17 +1,17 @@ ---- -uid: Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json -summary: *content ---- -When your Carter application needs the full power of Newtonsoft.Json—custom contract resolvers, `StringEnumConverter`, `JsonIgnore` attributes, or legacy compatibility mode—reach for the `Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json` namespace. The central type, `NewtonsoftJsonNegotiator`, handles JSON response serialization through the Carter `HttpResponse.Negotiate()` pipeline. - -This negotiator registers the `application/json` media type and uses a configurable `NewtonsoftJsonFormatterOptions` to control serialization settings, supported media types, and exception-descriptor formatting. It integrates with ASP.NET Core's options pattern and supports the `Accept-Charset` header for character-encoding negotiation. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -### When to use - -Choose `NewtonsoftJsonNegotiator` when you depend on Newtonsoft.Json features that System.Text.Json does not provide—such as `TypeNameHandling`, custom `JsonConverter` pipelines, `PreserveReferencesHandling`, or `ISerializable` support. This negotiator is also the right choice when migrating an existing Newtonsoft.Json-based codebase to Carter without changing your serialization stack. - -### Start here - -Install the `Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json` NuGet package, then register `NewtonsoftJsonNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. +--- +uid: Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json +summary: *content +--- +When your Carter application needs the full power of Newtonsoft.Json—custom contract resolvers, `StringEnumConverter`, `JsonIgnore` attributes, or legacy compatibility mode—reach for the `Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json` namespace. The central type, `NewtonsoftJsonNegotiator`, handles JSON response serialization through the Carter `HttpResponse.Negotiate()` pipeline. + +This negotiator registers the `application/json` media type and uses a configurable `NewtonsoftJsonFormatterOptions` to control serialization settings, supported media types, and exception-descriptor formatting. It integrates with ASP.NET Core's options pattern and supports the `Accept-Charset` header for character-encoding negotiation. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +### When to use + +Choose `NewtonsoftJsonNegotiator` when you depend on Newtonsoft.Json features that System.Text.Json does not provide—such as `TypeNameHandling`, custom `JsonConverter` pipelines, `PreserveReferencesHandling`, or `ISerializable` support. This negotiator is also the right choice when migrating an existing Newtonsoft.Json-based codebase to Carter without changing your serialization stack. + +### Start here + +Install the `Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json` NuGet package, then register `NewtonsoftJsonNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Json.md b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Json.md index 01a6b24..fd365ea 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Json.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Json.md @@ -1,17 +1,17 @@ ---- -uid: Codebelt.Extensions.Carter.AspNetCore.Text.Json -summary: *content ---- -Use the `Codebelt.Extensions.Carter.AspNetCore.Text.Json` namespace when your Carter application needs JSON response serialization backed by `System.Text.Json`—the high-performance, allocation-friendly JSON serializer built into the ASP.NET Core runtime. The `JsonResponseNegotiator` type integrates this serializer into the Carter `HttpResponse.Negotiate()` pipeline. - -This negotiator uses the `JsonFormatterOptions` to configure serialization behavior, supported media types, and exception-descriptor formatting. It respects the `Accept` and `Accept-Charset` headers for content-type and encoding negotiation. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -### When to use - -Choose `JsonResponseNegotiator` when you prefer the modern `System.Text.Json` stack for its performance characteristics and native ASP.NET Core integration, when you have no dependency on Newtonsoft.Json-specific features, or when you want to minimize external dependencies beyond the ASP.NET Core framework. - -### Start here - -Install the `Codebelt.Extensions.Carter.AspNetCore.Text.Json` NuGet package, then register `JsonResponseNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. +--- +uid: Codebelt.Extensions.Carter.AspNetCore.Text.Json +summary: *content +--- +Use the `Codebelt.Extensions.Carter.AspNetCore.Text.Json` namespace when your Carter application needs JSON response serialization backed by `System.Text.Json`—the high-performance, allocation-friendly JSON serializer built into the ASP.NET Core runtime. The `JsonResponseNegotiator` type integrates this serializer into the Carter `HttpResponse.Negotiate()` pipeline. + +This negotiator uses the `JsonFormatterOptions` to configure serialization behavior, supported media types, and exception-descriptor formatting. It respects the `Accept` and `Accept-Charset` headers for content-type and encoding negotiation. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +### When to use + +Choose `JsonResponseNegotiator` when you prefer the modern `System.Text.Json` stack for its performance characteristics and native ASP.NET Core integration, when you have no dependency on Newtonsoft.Json-specific features, or when you want to minimize external dependencies beyond the ASP.NET Core framework. + +### Start here + +Install the `Codebelt.Extensions.Carter.AspNetCore.Text.Json` NuGet package, then register `JsonResponseNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.md b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.md index 2f1265a..93ea6f1 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.md @@ -1,17 +1,17 @@ ---- -uid: Codebelt.Extensions.Carter.AspNetCore.Text.Yaml -summary: *content ---- -Enable YAML response negotiation in your Carter application with the `Codebelt.Extensions.Carter.AspNetCore.Text.Yaml` namespace. The `YamlResponseNegotiator` type, powered by YamlDotNet, lets your endpoints produce YAML responses through the standard `HttpResponse.Negotiate()` pipeline. - -This negotiator uses `YamlFormatterOptions` to configure serialization settings, supported media types, and character encoding. It is well-suited for tooling, configuration APIs, DevOps scenarios, and any environment where YAML is the preferred interchange format. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -### When to use - -Choose `YamlResponseNegotiator` when your API consumers expect YAML-formatted responses—common in CI/CD tooling, configuration management, infrastructure-as-code services, and cross-platform build pipelines where YAML's human readability and comment support add value over JSON. - -### Start here - -Install the `Codebelt.Extensions.Carter.AspNetCore.Text.Yaml` NuGet package, then register `YamlResponseNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. +--- +uid: Codebelt.Extensions.Carter.AspNetCore.Text.Yaml +summary: *content +--- +Enable YAML response negotiation in your Carter application with the `Codebelt.Extensions.Carter.AspNetCore.Text.Yaml` namespace. The `YamlResponseNegotiator` type, powered by YamlDotNet, lets your endpoints produce YAML responses through the standard `HttpResponse.Negotiate()` pipeline. + +This negotiator uses `YamlFormatterOptions` to configure serialization settings, supported media types, and character encoding. It is well-suited for tooling, configuration APIs, DevOps scenarios, and any environment where YAML is the preferred interchange format. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +### When to use + +Choose `YamlResponseNegotiator` when your API consumers expect YAML-formatted responses—common in CI/CD tooling, configuration management, infrastructure-as-code services, and cross-platform build pipelines where YAML's human readability and comment support add value over JSON. + +### Start here + +Install the `Codebelt.Extensions.Carter.AspNetCore.Text.Yaml` NuGet package, then register `YamlResponseNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Xml.md b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Xml.md index af7295b..281b9ba 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Xml.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Carter.AspNetCore.Xml.md @@ -1,17 +1,17 @@ ---- -uid: Codebelt.Extensions.Carter.AspNetCore.Xml -summary: *content ---- -Add XML response capability to your Carter application with the `Codebelt.Extensions.Carter.AspNetCore.Xml` namespace. The `XmlResponseNegotiator` type, built on `System.Xml.XmlWriter`, produces standards-compliant XML responses through the Carter `HttpResponse.Negotiate()` pipeline. - -This negotiator uses `XmlFormatterOptions` to configure XML writer settings, supported media types, and exception-descriptor formatting. It supports the `Accept` and `Accept-Charset` headers for content-type and encoding negotiation, and is suitable for legacy interoperability, SOA-style APIs, and enterprise integration scenarios. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -### When to use - -Choose `XmlResponseNegotiator` when your API must interoperate with legacy systems that require XML, when the API contract is defined by XSD schemas, or when you need standards-compliant XML output for enterprise integration workflows (e.g., SOAP-adjacent services, BizTalk, or SAP connectors). - -### Start here - -Install the `Codebelt.Extensions.Carter.AspNetCore.Xml` NuGet package, then register `XmlResponseNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. +--- +uid: Codebelt.Extensions.Carter.AspNetCore.Xml +summary: *content +--- +Add XML response capability to your Carter application with the `Codebelt.Extensions.Carter.AspNetCore.Xml` namespace. The `XmlResponseNegotiator` type, built on `System.Xml.XmlWriter`, produces standards-compliant XML responses through the Carter `HttpResponse.Negotiate()` pipeline. + +This negotiator uses `XmlFormatterOptions` to configure XML writer settings, supported media types, and exception-descriptor formatting. It supports the `Accept` and `Accept-Charset` headers for content-type and encoding negotiation, and is suitable for legacy interoperability, SOA-style APIs, and enterprise integration scenarios. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +### When to use + +Choose `XmlResponseNegotiator` when your API must interoperate with legacy systems that require XML, when the API contract is defined by XSD schemas, or when you need standards-compliant XML output for enterprise integration workflows (e.g., SOAP-adjacent services, BizTalk, or SAP connectors). + +### Start here + +Install the `Codebelt.Extensions.Carter.AspNetCore.Xml` NuGet package, then register `XmlResponseNegotiator` as the response negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Carter.Response.md b/.docfx/api/namespaces/Codebelt.Extensions.Carter.Response.md index 8726803..637a207 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Carter.Response.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Carter.Response.md @@ -1,4 +1,4 @@ ---- +--- uid: Codebelt.Extensions.Carter.Response summary: *content --- diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Carter.md b/.docfx/api/namespaces/Codebelt.Extensions.Carter.md index a61ffaa..f536abf 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Carter.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Carter.md @@ -1,24 +1,24 @@ ---- -uid: Codebelt.Extensions.Carter -summary: *content ---- -Build Carter-based ASP.NET Core minimal APIs with configurable response negotiation and endpoint metadata. The `Codebelt.Extensions.Carter` namespace gives you the infrastructure to plug content-aware response serialization into the Carter pipeline—letting `HttpResponse.Negotiate()` automatically select the right serializer based on the client's Accept header. - -Use this namespace when you want to:
-- Add OpenAPI/OpenAPI-like produces metadata to your endpoints using `Produces` and `Produces` extension methods.
-- Build or integrate a custom response negotiator via `ConfigurableResponseNegotiator`.
-- Choose from format-specific negotiator packages for JSON (Newtonsoft.Json or System.Text.Json), YAML, or XML. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -### Start here - -For most applications, install one of the format-specific negotiator packages (e.g., `Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json`) and register the negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. Then add produces metadata to your endpoints using the `Produces` extension method on `IEndpointConventionBuilder`. - -If you need a custom serialization format, derive from `ConfigurableResponseNegotiator` in the `Codebelt.Extensions.Carter.Response` namespace and supply your own formatter. - -### Extension Members - -|Type|Ext|Methods| -|--:|:-:|---| -|IEndpointConventionBuilder|⬇️|`Produces`, `Produces`| +--- +uid: Codebelt.Extensions.Carter +summary: *content +--- +Build Carter-based ASP.NET Core minimal APIs with configurable response negotiation and endpoint metadata. The `Codebelt.Extensions.Carter` namespace gives you the infrastructure to plug content-aware response serialization into the Carter pipeline—letting `HttpResponse.Negotiate()` automatically select the right serializer based on the client's Accept header. + +Use this namespace when you want to:
+- Add OpenAPI/OpenAPI-like produces metadata to your endpoints using `Produces` and `Produces` extension methods.
+- Build or integrate a custom response negotiator via `ConfigurableResponseNegotiator`.
+- Choose from format-specific negotiator packages for JSON (Newtonsoft.Json or System.Text.Json), YAML, or XML. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +### Start here + +For most applications, install one of the format-specific negotiator packages (e.g., `Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json`) and register the negotiator in your Carter configuration via `CarterConfigurator.WithResponseNegotiator()`. Then add produces metadata to your endpoints using the `Produces` extension method on `IEndpointConventionBuilder`. + +If you need a custom serialization format, derive from `ConfigurableResponseNegotiator` in the `Codebelt.Extensions.Carter.Response` namespace and supply your own formatter. + +### Extension Members + +|Type|Ext|Methods| +|--:|:-:|---| +|IEndpointConventionBuilder|⬇️|`Produces`, `Produces`| diff --git a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.NewtonsoftJsonNegotiator.md b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.NewtonsoftJsonNegotiator.md index 7e355e7..0f24d57 100644 --- a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.NewtonsoftJsonNegotiator.md +++ b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.NewtonsoftJsonNegotiator.md @@ -1,4 +1,4 @@ ---- +--- uid: Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.NewtonsoftJsonNegotiator summary: *content --- diff --git a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Json.JsonResponseNegotiator.md b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Json.JsonResponseNegotiator.md index 363f92e..180f2fd 100644 --- a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Json.JsonResponseNegotiator.md +++ b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Json.JsonResponseNegotiator.md @@ -1,4 +1,4 @@ ---- +--- uid: Codebelt.Extensions.Carter.AspNetCore.Text.Json.JsonResponseNegotiator summary: *content --- diff --git a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.YamlResponseNegotiator.md b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.YamlResponseNegotiator.md index 5aab63b..0c4aa58 100644 --- a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.YamlResponseNegotiator.md +++ b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.YamlResponseNegotiator.md @@ -1,4 +1,4 @@ ---- +--- uid: Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.YamlResponseNegotiator summary: *content --- diff --git a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Xml.XmlResponseNegotiator.md b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Xml.XmlResponseNegotiator.md index e9714b7..1ada18e 100644 --- a/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Xml.XmlResponseNegotiator.md +++ b/.docfx/api/types/Codebelt.Extensions.Carter.AspNetCore.Xml.XmlResponseNegotiator.md @@ -1,4 +1,4 @@ ---- +--- uid: Codebelt.Extensions.Carter.AspNetCore.Xml.XmlResponseNegotiator summary: *content --- diff --git a/.docfx/api/types/Codebelt.Extensions.Carter.EndpointConventionBuilderExtensions.md b/.docfx/api/types/Codebelt.Extensions.Carter.EndpointConventionBuilderExtensions.md index 87cad17..55002cb 100644 --- a/.docfx/api/types/Codebelt.Extensions.Carter.EndpointConventionBuilderExtensions.md +++ b/.docfx/api/types/Codebelt.Extensions.Carter.EndpointConventionBuilderExtensions.md @@ -1,4 +1,4 @@ ---- +--- uid: Codebelt.Extensions.Carter.EndpointConventionBuilderExtensions summary: *content --- diff --git a/.docfx/docfx.json b/.docfx/docfx.json index 7c88d72..7ebdab2 100644 --- a/.docfx/docfx.json +++ b/.docfx/docfx.json @@ -1,98 +1,98 @@ -{ - "metadata": [ - { - "src": [ - { - "files": [ - "Codebelt.Extensions.Carter/**.csproj", - "Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/**.csproj", - "Codebelt.Extensions.Carter.AspNetCore.Text.Json/**.csproj", - "Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/**.csproj", - "Codebelt.Extensions.Carter.AspNetCore.Xml/**.csproj" - ], - "src": "../src" - } - ], - "dest": "api", - "memberLayout": "separatePages", - "filter": "filterConfig.yml", - "properties": { - "TargetFramework": "net10.0" - } - } - ], - "build": { - "sitemap": { - "changefreq": "monthly", - "baseUrl": "https://carter.codebelt.net/"}, - "xref": [ - "https://docs.cuemon.net/xrefmap.yml?v=20260120", - "https://newtonsoft.codebelt.net/xrefmap.yml", - "https://yamldotnet.codebelt.net/xrefmap.yml", - "https://github.com/dotnet/docfx/raw/main/.xrefmap.json" - ], - "content": [ - { - "files": [ - "api/**/*.yml", - "packages/**/*.md", - "toc.yml", - "*.md" - ], - "exclude": [ - "bin/**", - "obj/**", - "api/namespaces/**", - "api/types/**" - ] - } - ], - "resource": [ - { - "files": [ - "images/**" - ] - } - ], - "globalMetadata": { - "_appTitle": "Extensions for Carter by Codebelt", - "_appFooter": "Generated by DocFX. Copyright 2026 Geekle. All rights reserved.", - "_appLogoPath": "images/50x50.png", - "_appFaviconPath": "images/favicon.ico", - "_googleAnalyticsTagId": "G-GGL98GEYWE", - "_enableSearch": false, - "_disableContribution": false, - "_gitContribute": { - "repo": "https://github.com/codebeltnet/carter", - "branch": "main" - }, - "_gitUrlPattern": "github" - }, - "dest": "wwwroot", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "template": [ - "default", - "modern", - "templates/ms-style" - ], - "overwrite": [ - { - "files": [ - "api/namespaces/**/*.md", - "api/types/**/*.md" - ], - "exclude": [ - "obj/**", - "wwwroot/**" - ] - } - ], - "postProcessors": [], - "markdownEngineName": "markdig", - "noLangKeyword": false, - "keepFileLink": false, - "cleanupCacheHistory": false, - "disableGitFeatures": false - } -} +{ + "metadata": [ + { + "src": [ + { + "files": [ + "Codebelt.Extensions.Carter/**.csproj", + "Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/**.csproj", + "Codebelt.Extensions.Carter.AspNetCore.Text.Json/**.csproj", + "Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/**.csproj", + "Codebelt.Extensions.Carter.AspNetCore.Xml/**.csproj" + ], + "src": "../src" + } + ], + "dest": "api", + "memberLayout": "separatePages", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "net10.0" + } + } + ], + "build": { + "sitemap": { + "changefreq": "monthly", + "baseUrl": "https://carter.codebelt.net/"}, + "xref": [ + "https://docs.cuemon.net/xrefmap.yml?v=20260120", + "https://newtonsoft.codebelt.net/xrefmap.yml", + "https://yamldotnet.codebelt.net/xrefmap.yml", + "https://github.com/dotnet/docfx/raw/main/.xrefmap.json" + ], + "content": [ + { + "files": [ + "api/**/*.yml", + "packages/**/*.md", + "toc.yml", + "*.md" + ], + "exclude": [ + "bin/**", + "obj/**", + "api/namespaces/**", + "api/types/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "globalMetadata": { + "_appTitle": "Extensions for Carter by Codebelt", + "_appFooter": "Generated by DocFX. Copyright 2026 Geekle. All rights reserved.", + "_appLogoPath": "images/50x50.png", + "_appFaviconPath": "images/favicon.ico", + "_googleAnalyticsTagId": "G-GGL98GEYWE", + "_enableSearch": false, + "_disableContribution": false, + "_gitContribute": { + "repo": "https://github.com/codebeltnet/carter", + "branch": "main" + }, + "_gitUrlPattern": "github" + }, + "dest": "wwwroot", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default", + "modern", + "templates/ms-style" + ], + "overwrite": [ + { + "files": [ + "api/namespaces/**/*.md", + "api/types/**/*.md" + ], + "exclude": [ + "obj/**", + "wwwroot/**" + ] + } + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false + } +} diff --git a/.docfx/includes/availability-modern.md b/.docfx/includes/availability-modern.md index 5ed3f53..22cf496 100644 --- a/.docfx/includes/availability-modern.md +++ b/.docfx/includes/availability-modern.md @@ -1 +1 @@ -Availability: .NET 10 \ No newline at end of file +Availability: .NET 10 and .NET 11 diff --git a/.docfx/index.md b/.docfx/index.md index 50d2310..f2fc6bc 100644 --- a/.docfx/index.md +++ b/.docfx/index.md @@ -1,11 +1,11 @@ ---- -uid: frontpage-md -title: Extensions for Carter by Codebelt ---- -![Extensions for Carter API by Codebelt](/images/128x128.png) - -# Extensions for Carter by Codebelt - -This project is part of [Extensions for Carter by Codebelt](https://github.com/codebeltnet/carter). - -Proceed to the [docs](/api/Codebelt.Extensions.Carter.html) to learn more about the capabilities of this project. +--- +uid: frontpage-md +title: Extensions for Carter by Codebelt +--- +![Extensions for Carter API by Codebelt](/images/128x128.png) + +# Extensions for Carter by Codebelt + +This project is part of [Extensions for Carter by Codebelt](https://github.com/codebeltnet/carter). + +Proceed to the [docs](/api/Codebelt.Extensions.Carter.html) to learn more about the capabilities of this project. diff --git a/.docfx/nginx.conf b/.docfx/nginx.conf index 60e3d08..d9861bd 100644 --- a/.docfx/nginx.conf +++ b/.docfx/nginx.conf @@ -1,31 +1,31 @@ server { - listen 8080; - server_name _; - # The ingress terminates TLS; keep redirects from exposing this container port. - absolute_redirect off; + listen 8080; + server_name _; + # The ingress terminates TLS; keep redirects from exposing this container port. + absolute_redirect off; - root /usr/share/nginx/html; - index index.html; + root /usr/share/nginx/html; + index index.html; - # DocFX fingerprints these generated dependencies in their filenames. They can be - # cached permanently because changed content is published under a different URL. - location ~ "^/public/.+-[A-Z0-9]{8}(?:-[A-Z0-9]{8})?(?:\.min)?\.(?:(?:css|js)(?:\.map)?|woff2?)$" { - try_files $uri =404; - add_header Cache-Control "public, max-age=31536000, immutable"; - add_header Vary "Accept-Encoding" always; - } + # DocFX fingerprints these generated dependencies in their filenames. They can be + # cached permanently because changed content is published under a different URL. + location ~ "^/public/.+-[A-Z0-9]{8}(?:-[A-Z0-9]{8})?(?:\.min)?\.(?:(?:css|js)(?:\.map)?|woff2?)$" { + try_files $uri =404; + add_header Cache-Control "public, max-age=31536000, immutable" always; + add_header Vary "Accept-Encoding" always; + } - # Pages, navigation data, images, and DocFX entry assets keep stable URLs. Do not - # retain them across deployments, and ignore validators cached under an old policy. - location / { - # Nginx derives ETags from modification time and file size. Those values can be - # reused by files copied from different image layers, producing a false 304. - etag off; - if_modified_since off; - try_files $uri $uri/ =404; - add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; - add_header Expires "0" always; - add_header Pragma "no-cache" always; - add_header Vary "Accept-Encoding" always; - } + # Pages, navigation data, images, and DocFX entry assets keep stable URLs. Do not + # retain them across deployments, and ignore validators cached under an old policy. + location / { + # Nginx derives ETags from modification time and file size. Those values can be + # reused by files copied from different image layers, producing a false 304. + etag off; + if_modified_since off; + try_files $uri $uri/ =404; + add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always; + add_header Expires "0" always; + add_header Pragma "no-cache" always; + add_header Vary "Accept-Encoding" always; + } } diff --git a/.docfx/toc.yml b/.docfx/toc.yml index ab77dcc..e5e3911 100644 --- a/.docfx/toc.yml +++ b/.docfx/toc.yml @@ -1,4 +1,4 @@ -- name: Carter API - href: api/Codebelt.Extensions.Carter.html -- name: NuGet - href: packages +- name: Carter API + href: api/Codebelt.Extensions.Carter.html +- name: NuGet + href: packages diff --git a/.editorconfig b/.editorconfig index 6d0e3c9..886ea15 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,176 +1,177 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true - -# Default: prefer spaces for data/markup -indent_style = space -indent_size = 2 -tab_width = 2 - -# This style rule concern the use of the range operator, which is available in C# 8.0 and later. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0057 -[*.{cs,vb}] -dotnet_diagnostic.IDE0057.severity = none - -# This style rule concerns the use of switch expressions versus switch statements. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0066 -[*.{cs,vb}] -dotnet_diagnostic.IDE0066.severity = none - -# Performance rules -# https://docs.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings -[*.{cs,vb}] -dotnet_analyzer_diagnostic.category-Performance.severity = none # Because many of the suggestions by performance analyzers are not compatible with .NET Standard 2.0 - -# This style rule concerns the use of using statements without curly braces, also known as using declarations. This alternative syntax was introduced in C# 8.0. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0063 -[*.{cs,vb}] -dotnet_diagnostic.IDE0063.severity = none - -# This style rule concerns with simplification of interpolated strings to improve code readability. It recommends removal of certain explicit method calls, such as ToString(), when the same method would be implicitly invoked by the compiler if the explicit method call is removed. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0071 -[*.{cs,vb}] -dotnet_diagnostic.IDE0071.severity = none - -# S3267: Loops should be simplified with "LINQ" expressions -# https://rules.sonarsource.com/csharp/RSPEC-3267 -dotnet_diagnostic.S3267.severity = none - -# CA1859: Use concrete types when possible for improved performance -# This is a violation of Framework Design Guidelines. -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859 -[*.{cs,vb}] -dotnet_diagnostic.CA1859.severity = none - -# IDE0008: Use explicit type -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007-ide0008 -[*.{cs,vb}] -dotnet_diagnostic.IDE0008.severity = none - -[*.{cs,vb}] -indent_style = space -indent_size = 4 - -# IDE0161: Namespace declaration preferences -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0161 -# Prefer file-scoped namespaces for new files; existing block-scoped files should not be converted unless explicitly asked -[*.cs] -csharp_style_namespace_declarations = file_scoped:suggestion - -# Top-level statements: DO NOT USE -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0210-ide0211 -# This is enforced via a style preference set to error severity, not a language-level prohibition. -# Always use explicit class declarations with a proper namespace and Main method where applicable. -[*.cs] -csharp_style_prefer_top_level_statements = false:error - -[*.xml] -indent_style = space -indent_size = 2 - -# IDE0078: Use pattern matching -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0078 -[*.{cs,vb}] -dotnet_diagnostic.IDE0078.severity = none - -# IDE0290: Use primary constructor -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290 -[*.{cs,vb}] -dotnet_diagnostic.IDE0290.severity = none - -# IDE0305: Use collection expression for fluent -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305 -[*.{cs,vb}] -dotnet_diagnostic.IDE0305.severity = none - -# IDE0011: Add braces -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0011 -[*.{cs,vb}] -dotnet_diagnostic.IDE0011.severity = none - -# IDE0028: Use collection initializers or expressions -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 -[*.{cs,vb}] -dotnet_diagnostic.IDE0028.severity = none - -# IDE0039: Use collection expression for array -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300 -[*.{cs,vb}] -dotnet_diagnostic.IDE0300.severity = none - -# IDE0031: Use collection expression for empty -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301 -[*.{cs,vb}] -dotnet_diagnostic.IDE0301.severity = none - -# IDE0046: Use conditional expression for return -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0046 -[*.{cs,vb}] -dotnet_diagnostic.IDE0046.severity = none - -# IDE0047: Parentheses preferences -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047-ide0048 -[*.{cs,vb}] -dotnet_diagnostic.IDE0047.severity = none - -# CA1716: Identifiers should not match keywords -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1716 -[*.{cs,vb}] -dotnet_diagnostic.CA1716.severity = none - -# CA1720: Identifiers should not contain type names -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1720 -[*.{cs,vb}] -dotnet_diagnostic.CA1720.severity = none - -# CA1846: Prefer AsSpan over Substring -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1846 -# Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] -dotnet_diagnostic.CA1846.severity = none - -# CA1847: Use String.Contains(char) instead of String.Contains(string) with single characters -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1847 -# Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] -dotnet_diagnostic.CA1847.severity = none - -# CA1865-CA1867: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1865-ca1867 -# Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] -dotnet_diagnostic.CA1865.severity = none -dotnet_diagnostic.CA1866.severity = none -dotnet_diagnostic.CA1867.severity = none - -# CA2263: Prefer generic overload when type is known -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2263 -# Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] -dotnet_diagnostic.CA2263.severity = none - -# CA2249: Consider using String.Contains instead of String.IndexOf -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2249 -# Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] -dotnet_diagnostic.CA2249.severity = none - -# IDE0022: Use expression body for methods -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0022 -[*.{cs,vb}] -dotnet_diagnostic.IDE0022.severity = none - -# IDE0032: Use auto-property -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0032 -[*.{cs,vb}] -dotnet_diagnostic.IDE0032.severity = none - -# Order modifiers -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 -# Excluded becuase of inconsistency with other analyzers -[*.{cs,vb}] -dotnet_diagnostic.IDE0036.severity = none +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# Default: prefer spaces for data/markup +indent_style = space +indent_size = 2 +tab_width = 2 + +# This style rule concern the use of the range operator, which is available in C# 8.0 and later. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0057 +[*.{cs,vb}] +dotnet_diagnostic.IDE0057.severity = none + +# This style rule concerns the use of switch expressions versus switch statements. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0066 +[*.{cs,vb}] +dotnet_diagnostic.IDE0066.severity = none + +# Performance rules +# https://docs.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings +[*.{cs,vb}] +dotnet_analyzer_diagnostic.category-Performance.severity = none # Because many of the suggestions by performance analyzers are not compatible with .NET Standard 2.0 + +# This style rule concerns the use of using statements without curly braces, also known as using declarations. This alternative syntax was introduced in C# 8.0. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0063 +[*.{cs,vb}] +dotnet_diagnostic.IDE0063.severity = none + +# This style rule concerns with simplification of interpolated strings to improve code readability. It recommends removal of certain explicit method calls, such as ToString(), when the same method would be implicitly invoked by the compiler if the explicit method call is removed. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0071 +[*.{cs,vb}] +dotnet_diagnostic.IDE0071.severity = none + +# S3267: Loops should be simplified with "LINQ" expressions +# https://sonarcloud.io/organizations/sonarcloud/rules?open=csharpsquid:S3267&rule_key=csharpsquid:S3267 +dotnet_diagnostic.S3267.severity = none + +# CA1859: Use concrete types when possible for improved performance +# This is a violation of Framework Design Guidelines. +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859 +[*.{cs,vb}] +dotnet_diagnostic.CA1859.severity = none + +# IDE0008: Use explicit type +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007-ide0008 +[*.{cs,vb}] +dotnet_diagnostic.IDE0008.severity = none + +[*.{cs,vb}] +indent_style = space +indent_size = 4 + +# IDE0161: Namespace declaration preferences +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0161 +# File-scoped namespaces are mandatory for new and existing C# files. +[*.cs] +csharp_style_namespace_declarations = file_scoped:error +dotnet_diagnostic.IDE0161.severity = error + +# Top-level statements: DO NOT USE +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0210-ide0211 +# This is enforced via a style preference set to error severity, not a language-level prohibition. +# Always use explicit class declarations with a proper namespace and Main method where applicable. +[*.cs] +csharp_style_prefer_top_level_statements = false:error + +[*.xml] +indent_style = space +indent_size = 2 + +# IDE0078: Use pattern matching +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0078 +[*.{cs,vb}] +dotnet_diagnostic.IDE0078.severity = none + +# IDE0290: Use primary constructor +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290 +[*.{cs,vb}] +dotnet_diagnostic.IDE0290.severity = none + +# IDE0305: Use collection expression for fluent +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305 +[*.{cs,vb}] +dotnet_diagnostic.IDE0305.severity = none + +# IDE0011: Add braces +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0011 +[*.{cs,vb}] +dotnet_diagnostic.IDE0011.severity = none + +# IDE0028: Use collection initializers or expressions +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 +[*.{cs,vb}] +dotnet_diagnostic.IDE0028.severity = none + +# IDE0039: Use collection expression for array +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300 +[*.{cs,vb}] +dotnet_diagnostic.IDE0300.severity = none + +# IDE0031: Use collection expression for empty +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301 +[*.{cs,vb}] +dotnet_diagnostic.IDE0301.severity = none + +# IDE0046: Use conditional expression for return +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0046 +[*.{cs,vb}] +dotnet_diagnostic.IDE0046.severity = none + +# IDE0047: Parentheses preferences +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047-ide0048 +[*.{cs,vb}] +dotnet_diagnostic.IDE0047.severity = none + +# CA1716: Identifiers should not match keywords +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1716 +[*.{cs,vb}] +dotnet_diagnostic.CA1716.severity = none + +# CA1720: Identifiers should not contain type names +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1720 +[*.{cs,vb}] +dotnet_diagnostic.CA1720.severity = none + +# CA1846: Prefer AsSpan over Substring +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1846 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA1846.severity = none + +# CA1847: Use String.Contains(char) instead of String.Contains(string) with single characters +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1847 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA1847.severity = none + +# CA1865-CA1867: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1865-ca1867 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA1865.severity = none +dotnet_diagnostic.CA1866.severity = none +dotnet_diagnostic.CA1867.severity = none + +# CA2263: Prefer generic overload when type is known +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2263 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA2263.severity = none + +# CA2249: Consider using String.Contains instead of String.IndexOf +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2249 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA2249.severity = none + +# IDE0022: Use expression body for methods +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0022 +[*.{cs,vb}] +dotnet_diagnostic.IDE0022.severity = none + +# IDE0032: Use auto-property +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0032 +[*.{cs,vb}] +dotnet_diagnostic.IDE0032.severity = none + +# Order modifiers +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 +# Excluded becuase of inconsistency with other analyzers +[*.{cs,vb}] +dotnet_diagnostic.IDE0036.severity = none diff --git a/.gitattributes b/.gitattributes index 1ff0c42..0ea8d1b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,7 @@ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### -* text=auto +* text=auto eol=lf ############################################################################### # Set default behavior for command prompt diff. @@ -17,7 +17,7 @@ # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following +# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below @@ -46,9 +46,9 @@ ############################################################################### # diff behavior for common document formats -# +# # Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the +# is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9b6d7a1..cc4eb18 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,79 +1,83 @@ -# Contributing to Extensions for Carter API by Codebelt - -This repository is part of the Codebelt .NET library estate. The instructions below describe the current checkout and its CI contract. Please keep changes focused and preserve the shared Codebelt build skeleton unless a deliberate policy change is being made. - -## Before you start - -- Read the repository `README.md` and open an issue before starting a non-trivial feature or behavioral change. -- Use an installed .NET SDK that can build the target frameworks listed below. This repository currently targets: **net10.0**. -- The solution is `Codebelt.Extensions.Carter.slnx`. Central package versions are maintained in `Directory.Packages.props`. -- The shared build behavior is in `Directory.Build.props` and `Directory.Build.targets`; repository-specific TFMs, package references and metadata remain local to this library. - -## Repository shape - -- `src/` contains production projects. -- `test/` contains xUnit v3 test projects. -- `Codebelt.Extensions.Carter.slnx` is the solution used for local development. -- `.github/workflows/ci-pipeline.yml` is the CI workflow and the authority for the test matrix. -- `testenvironments.json` declares the supported `WSL-Ubuntu` and `Docker-Ubuntu` test environments. - -## Build - -Restore and build the solution from the repository root: - -```powershell -dotnet restore "Codebelt.Extensions.Carter.slnx" -dotnet build "Codebelt.Extensions.Carter.slnx" --configuration Release --no-restore -``` - -CI builds both Debug and Release configurations. A clean build should complete before opening a pull request. - -## Test - -Run tests one project at a time so a failing or hanging project is attributable. This mirrors the CI matrix; it does not silently turn skipped integration tests into passing tests. - -```powershell -$testProjects = Get-ChildItem test -Filter *.csproj -Recurse -foreach ($project in $testProjects) { - dotnet test $project.FullName --configuration Release --no-restore -} -``` - -The CI test plan currently runs **6** project(s) and excludes **0** project(s). The workflow also has an optional macOS test job. - -## Integration and infrastructure - -- `WSL-Ubuntu` — WSL distribution `Ubuntu-24.04`. -- `Docker-Ubuntu` — Docker image `codebeltnet/ubuntu-testrunner:8-9-10-11`. - -This repository has no repository-local `docker-compose.yml` service dependency in the current checkout. -Use the environments declared in `testenvironments.json` when you need the estate test runner. - -## Package and documentation - -Create packages using the same solution and Release configuration: - -```powershell -dotnet pack "Codebelt.Extensions.Carter.slnx" --configuration Release --no-restore -``` - -Package-specific release notes live under `.nuget//PackageReleaseNotes.txt` and package README files live beside them. `Directory.Build.targets` imports the release notes during packing. Public API changes also require XML documentation updates; DocFX documentation is built by the repository automation. - -## Pull requests - -1. Create or join an issue before substantial work, then fork the repository and create a branch from `main`. -2. Add or update focused tests and public API documentation where applicable. -3. Run restore, build, and the relevant per-project tests locally. -4. Keep the pull request small, explain the behavior change and validation performed, and wait for the CI checks to pass. - -## Issues - -Include the affected project, target framework, operating system, SDK version, exact command, expected result, actual result, and a minimal reproduction. Identify whether the behavior differs between local Windows/WSL, Docker-Ubuntu and GitHub Actions. - -## Coding guidelines - -Follow the existing style, the Framework Design Guidelines, the repository `.editorconfig`, and the shared Codebelt conventions. Do not make unrelated formatting or infrastructure changes in a feature pull request. - -## License - -By contributing to Extensions for Carter API by Codebelt, you agree that your contributions will be licensed under the MIT license. +# Contributing to Extensions for Carter API by Codebelt + +This repository is part of the Codebelt .NET library estate. The instructions below describe the current checkout and its CI contract. Please keep changes focused and preserve the shared Codebelt build skeleton unless a deliberate policy change is being made. + +## Before you start + +- Read the repository `README.md` and open an issue before starting a non-trivial feature or behavioral change. +- Install the .NET 10 and .NET 11 SDKs to build **net10.0** and **net11.0** and run all tests. This supplies the required .NET and ASP.NET Core runtimes for both targets. +- The solution is `Codebelt.Extensions.Carter.slnx`. Central package versions are maintained in `Directory.Packages.props`. +- The shared build behavior is in `Directory.Build.props` and `Directory.Build.targets`; repository-specific TFMs, package references and metadata remain local to this library. + +## Repository shape + +- `src/` contains production projects. +- `test/` contains xUnit v3 test projects. +- `Codebelt.Extensions.Carter.slnx` is the solution used for local development. +- `.github/workflows/ci-pipeline.yml` is the CI workflow and the authority for the test matrix. +- `testenvironments.json` declares the supported `WSL-Ubuntu` and `Docker-Ubuntu` test environments. + +## Build + +Restore and build the solution from the repository root: + +```powershell +dotnet restore "Codebelt.Extensions.Carter.slnx" +dotnet build "Codebelt.Extensions.Carter.slnx" --configuration Release --no-restore +``` + +CI builds both Debug and Release configurations. A clean build should complete before opening a pull request. + +## Test + +Run tests one project at a time so a failing or hanging project is attributable. This mirrors the CI matrix; it does not silently turn skipped integration tests into passing tests. + +```powershell +$testProjects = Get-ChildItem test -Filter *.csproj -Recurse +foreach ($project in $testProjects) { + dotnet test --project $project.FullName --configuration Release --no-build --no-restore + if ($LASTEXITCODE -ne 0) { throw "Tests failed: $($project.FullName)" } +} +``` + +The CI test plan runs all **6** test projects on both **net10.0** and **net11.0**, in Debug and Release on Linux and Windows for x64 and ARM64. The workflow also has an optional macOS test job. SDK installation includes preview releases so .NET 11 can be tested before its final release. + +NuGet publishing requires successful builds, packaging, the test quality gate, SonarCloud, Codecov, and CodeQL. + +## Integration and infrastructure + +- `WSL-Ubuntu` — WSL distribution `Ubuntu-24.04`. +- `Docker-Ubuntu (net10.0)` — Docker image `codebeltnet/ubuntu-testrunner:10`. +- `Docker-Ubuntu (net11.0)` — Docker image `codebeltnet/ubuntu-testrunner:11`. + +This repository has no repository-local `docker-compose.yml` service dependency in the current checkout. +Use the environments declared in `testenvironments.json` when you need the estate test runner. + +## Package and documentation + +Create packages using the same solution and Release configuration: + +```powershell +dotnet pack "Codebelt.Extensions.Carter.slnx" --configuration Release --no-restore +``` + +Package-specific release notes live under `.nuget//PackageReleaseNotes.txt` and package README files live beside them. `Directory.Build.targets` imports the release notes during packing. Public API changes also require XML documentation updates; DocFX documentation is built by the repository automation. + +## Pull requests + +1. Create or join an issue before substantial work, then fork the repository and create a branch from `main`. +2. Add or update focused tests and public API documentation where applicable. +3. Run restore, build, and the relevant per-project tests locally. +4. Keep the pull request small, explain the behavior change and validation performed, and wait for the CI checks to pass. + +## Issues + +Include the affected project, target framework, operating system, SDK version, exact command, expected result, actual result, and a minimal reproduction. Identify whether the behavior differs between local Windows/WSL, Docker-Ubuntu and GitHub Actions. + +## Coding guidelines + +Follow the existing style, the Framework Design Guidelines, the repository `.editorconfig`, and the shared Codebelt conventions. Do not make unrelated formatting or infrastructure changes in a feature pull request. + +## License + +By contributing to Extensions for Carter API by Codebelt, you agree that your contributions will be licensed under the MIT license. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1fca42a..7fe3850 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,17 +1,17 @@ -version: 2 -updates: - - package-ecosystem: "nuget" - directory: "/src" - schedule: - interval: "weekly" - open-pull-requests-limit: 0 - - package-ecosystem: "nuget" - directory: "/test" - schedule: - interval: "weekly" - open-pull-requests-limit: 0 - - package-ecosystem: "github-actions" - directory: "/.github/workflows" - schedule: - interval: "weekly" - open-pull-requests-limit: 0 +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/src" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 + - package-ecosystem: "nuget" + directory: "/test" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 + - package-ecosystem: "github-actions" + directory: "/.github/workflows" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 09c295c..55f2117 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -23,7 +23,7 @@ permissions: jobs: init: name: initialize - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 outputs: run-mac-tests: ${{ steps.vars.outputs.run-mac-tests }} run-privileged-jobs: ${{ steps.vars.outputs.run-privileged-jobs }} @@ -59,10 +59,11 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-build/.github/workflows/default.yml@v3 with: + include-preview: true configuration: ${{ matrix.configuration }} strong-name-key-filename: ${{ needs.init.outputs.strong-name-key-filename }} build-switches: ${{ needs.init.outputs.build-switches }} - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} upload-build-artifact-name: build-${{ matrix.configuration }}-${{ matrix.arch }} secrets: inherit @@ -74,6 +75,7 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-pack/.github/workflows/default.yml@v3 with: + include-preview: true configuration: ${{ matrix.configuration }} version: ${{ needs.build.outputs.version }} download-build-artifact-pattern: build-${{ matrix.configuration }}-X64 @@ -88,7 +90,8 @@ jobs: arch: [X64, ARM64] uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + include-preview: true + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true build: true # we need to build due to xUnitv3 @@ -105,6 +108,7 @@ jobs: arch: [X64, ARM64] uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'windows-11-arm' || 'windows-2025' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true @@ -123,6 +127,7 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'macos-26' || 'macos-26-intel' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true @@ -134,7 +139,7 @@ jobs: if: ${{ always() }} name: test-qualitygate needs: [init, test_linux, test_windows, test_mac] - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Evaluate test results shell: bash @@ -180,6 +185,7 @@ jobs: needs: [init, build, test_qualitygate] uses: codebeltnet/jobs-sonarcloud/.github/workflows/default.yml@v3 with: + include-preview: true organization: geekle projectKey: codebelt-carter version: ${{ needs.build.outputs.version }} @@ -199,6 +205,8 @@ jobs: name: call-codeql needs: [init, build, test_qualitygate] uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3 + with: + include-preview: true permissions: security-events: write diff --git a/.github/workflows/service-update.yml b/.github/workflows/service-update.yml index 1f69586..f55bbc4 100644 --- a/.github/workflows/service-update.yml +++ b/.github/workflows/service-update.yml @@ -125,7 +125,7 @@ jobs: NEW="${{ steps.newver.outputs.new }}" for f in .nuget/*/PackageReleaseNotes.txt; do [ -f "$f" ] || continue - TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10, .NET 9 and .NET Standard 2.0") + TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 11 and .NET 10") ENTRY="Version: ${NEW}\nAvailability: ${TFM}\n\n# ALM\n- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)\n\n" { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f" done diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt index 5bdbb50..a080b03 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt @@ -1,59 +1,65 @@ +Version: 1.1.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) so the package now targets .NET 11 and .NET 10 + Version: 1.0.9 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.8 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.7 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.6 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.5 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.4 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.3 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.2 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.1 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.0 -Availability: .NET 10 - -# New Features -- ADDED NewtonsoftJsonNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json namespace that provides a JSON response negotiator for Carter, capable of serializing response models to JSON format using Newtonsoft.Json +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.2 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.1 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.0 +Availability: .NET 10 + +# New Features +- ADDED NewtonsoftJsonNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json namespace that provides a JSON response negotiator for Carter, capable of serializing response models to JSON format using Newtonsoft.Json diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/README.md b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/README.md index 6260aa8..3d435a8 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/README.md +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/README.md @@ -1,79 +1,81 @@ -# Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json - -A Newtonsoft.Json-powered response negotiator for Carter in ASP.NET Core minimal APIs. - -## About - -**Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json** extends the **Codebelt.Extensions.Carter** package with a dedicated JSON response negotiator for Carter, capable of serializing response models to JSON format using Newtonsoft.Json. - -This package is useful when you need Newtonsoft.Json-specific behavior (for example custom converters, contract resolvers, or legacy JSON compatibility) while keeping Carter modules and content negotiation straightforward. - -## CSharp Example - -Functional-test style sample (same bootstrapping pattern used by this repository): - -```csharp -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; -using Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; - -using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddNewtonsoftJsonFormatterOptions(o => - { - o.Settings.Converters.Insert(0, new StringEnumConverter(new DefaultNamingStrategy(), false)); - }); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - return await client.GetAsync("/world/statistical-regions"); - }); -``` - -Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): - -```csharp -using Carter; -using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; -using Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddNewtonsoftJsonFormatterOptions(); -builder.Services.AddCarter(c => c - .WithResponseNegotiator()); - -var app = builder.Build(); -app.MapCarter(); -app.Run(); -``` - -## Related Packages - -* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 +# Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json + +A Newtonsoft.Json-powered response negotiator for Carter in ASP.NET Core minimal APIs. + +Target frameworks: .NET 10 (`net10.0`) and .NET 11 (`net11.0`). + +## About + +**Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json** extends the **Codebelt.Extensions.Carter** package with a dedicated JSON response negotiator for Carter, capable of serializing response models to JSON format using Newtonsoft.Json. + +This package is useful when you need Newtonsoft.Json-specific behavior (for example custom converters, contract resolvers, or legacy JSON compatibility) while keeping Carter modules and content negotiation straightforward. + +## CSharp Example + +Functional-test style sample (same bootstrapping pattern used by this repository): + +```csharp +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; +using Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddNewtonsoftJsonFormatterOptions(o => + { + o.Settings.Converters.Insert(0, new StringEnumConverter(new DefaultNamingStrategy(), false)); + }); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return await client.GetAsync("/world/statistical-regions"); + }); +``` + +Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): + +```csharp +using Carter; +using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; +using Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddNewtonsoftJsonFormatterOptions(); +builder.Services.AddCarter(c => c + .WithResponseNegotiator()); + +var app = builder.Build(); +app.MapCarter(); +app.Run(); +``` + +## Related Packages + +* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/PackageReleaseNotes.txt index b112870..2dccd39 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/PackageReleaseNotes.txt @@ -1,59 +1,59 @@ -Version: 1.0.9 -Availability: .NET 10 +Version: 1.1.0 +Availability: .NET 11 and .NET 10 # ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) +- CHANGED Target frameworks by adding .NET 11 (net11.0) so the package now targets .NET 11 and .NET 10 Version: 1.0.8 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.7 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.6 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.5 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.4 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.3 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.2 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.1 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.0 -Availability: .NET 10 - -# New Features -- ADDED JsonResponseNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Text.Json namespace that provides a JSON response negotiator for Carter, capable of serializing response models to JSON format using System.Text.Json +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.2 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.1 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.0 +Availability: .NET 10 + +# New Features +- ADDED JsonResponseNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Text.Json namespace that provides a JSON response negotiator for Carter, capable of serializing response models to JSON format using System.Text.Json diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/README.md b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/README.md index 0928d38..ab57751 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/README.md +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Json/README.md @@ -1,78 +1,80 @@ -# Codebelt.Extensions.Carter.AspNetCore.Text.Json - -A System.Text.Json-powered response negotiator for Carter in ASP.NET Core minimal APIs. - -## About - -**Codebelt.Extensions.Carter.AspNetCore.Text.Json** extends the **Codebelt.Extensions.Carter** package with a dedicated JSON response negotiator for Carter, capable of serializing response models to JSON format using System.Text.Json. - -Use this package when you want a lightweight, built-in JSON stack with Carter response negotiation and configurable serializer options. - -## CSharp Example - -Functional-test style sample (same bootstrapping pattern used by this repository): - -```csharp -using System.Net.Http.Headers; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.Carter.AspNetCore.Text.Json; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.AspNetCore.Text.Json; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalJsonOptions(o => - { - o.Settings.Converters.Insert(0, new JsonStringEnumConverter()); - }); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - return await client.GetAsync("/world/statistical-regions"); - }); -``` - -Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): - -```csharp -using Carter; -using Codebelt.Extensions.Carter.AspNetCore.Text.Json; -using Cuemon.Extensions.AspNetCore.Text.Json; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddMinimalJsonOptions(); -builder.Services.AddCarter(c => c - .WithResponseNegotiator()); - -var app = builder.Build(); -app.MapCarter(); -app.Run(); -``` - -## Related Packages - -* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 +# Codebelt.Extensions.Carter.AspNetCore.Text.Json + +A System.Text.Json-powered response negotiator for Carter in ASP.NET Core minimal APIs. + +Target frameworks: .NET 10 (`net10.0`) and .NET 11 (`net11.0`). + +## About + +**Codebelt.Extensions.Carter.AspNetCore.Text.Json** extends the **Codebelt.Extensions.Carter** package with a dedicated JSON response negotiator for Carter, capable of serializing response models to JSON format using System.Text.Json. + +Use this package when you want a lightweight, built-in JSON stack with Carter response negotiation and configurable serializer options. + +## CSharp Example + +Functional-test style sample (same bootstrapping pattern used by this repository): + +```csharp +using System.Net.Http.Headers; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.Carter.AspNetCore.Text.Json; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.AspNetCore.Text.Json; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalJsonOptions(o => + { + o.Settings.Converters.Insert(0, new JsonStringEnumConverter()); + }); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return await client.GetAsync("/world/statistical-regions"); + }); +``` + +Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): + +```csharp +using Carter; +using Codebelt.Extensions.Carter.AspNetCore.Text.Json; +using Cuemon.Extensions.AspNetCore.Text.Json; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddMinimalJsonOptions(); +builder.Services.AddCarter(c => c + .WithResponseNegotiator()); + +var app = builder.Build(); +app.MapCarter(); +app.Run(); +``` + +## Related Packages + +* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/PackageReleaseNotes.txt index cfb0c79..40cfbc2 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/PackageReleaseNotes.txt @@ -1,59 +1,59 @@ -Version: 1.0.9 -Availability: .NET 10 +Version: 1.1.0 +Availability: .NET 11 and .NET 10 # ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) +- CHANGED Target frameworks by adding .NET 11 (net11.0) so the package now targets .NET 11 and .NET 10 Version: 1.0.8 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.7 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.6 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.5 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.4 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.3 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.2 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.1 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.0 -Availability: .NET 10 - -# New Features -- ADDED YamlResponseNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Text.Yaml namespace that provides a YAML response negotiator for Carter, capable of serializing response models to YAML format using YamlDotNet +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.2 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.1 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.0 +Availability: .NET 10 + +# New Features +- ADDED YamlResponseNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Text.Yaml namespace that provides a YAML response negotiator for Carter, capable of serializing response models to YAML format using YamlDotNet diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/README.md b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/README.md index c413bd9..bc0d864 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/README.md +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/README.md @@ -1,73 +1,75 @@ -# Codebelt.Extensions.Carter.AspNetCore.Text.Yaml - -A YamlDotNet-powered response negotiator for Carter in ASP.NET Core minimal APIs. - -## About - -**Codebelt.Extensions.Carter.AspNetCore.Text.Yaml** extends the **Codebelt.Extensions.Carter** package with a dedicated YAML response negotiator for Carter, capable of serializing response models to YAML format using YamlDotNet. - -Use this package when your API clients require YAML responses and you want to keep Carter modules and content negotiation clean and explicit. - -## CSharp Example - -Functional-test style sample (same bootstrapping pattern used by this repository): - -```csharp -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalYamlOptions(); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/yaml")); - return await client.GetAsync("/world/statistical-regions"); - }); -``` - -Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): - -```csharp -using Carter; -using Codebelt.Extensions.AspNetCore.Text.Yaml.Formatters; -using Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddMinimalYamlOptions(); -builder.Services.AddCarter(c => c - .WithResponseNegotiator()); - -var app = builder.Build(); -app.MapCarter(); -app.Run(); -``` - -## Related Packages - -* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 +# Codebelt.Extensions.Carter.AspNetCore.Text.Yaml + +A YamlDotNet-powered response negotiator for Carter in ASP.NET Core minimal APIs. + +Target frameworks: .NET 10 (`net10.0`) and .NET 11 (`net11.0`). + +## About + +**Codebelt.Extensions.Carter.AspNetCore.Text.Yaml** extends the **Codebelt.Extensions.Carter** package with a dedicated YAML response negotiator for Carter, capable of serializing response models to YAML format using YamlDotNet. + +Use this package when your API clients require YAML responses and you want to keep Carter modules and content negotiation clean and explicit. + +## CSharp Example + +Functional-test style sample (same bootstrapping pattern used by this repository): + +```csharp +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalYamlOptions(); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/yaml")); + return await client.GetAsync("/world/statistical-regions"); + }); +``` + +Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): + +```csharp +using Carter; +using Codebelt.Extensions.AspNetCore.Text.Yaml.Formatters; +using Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddMinimalYamlOptions(); +builder.Services.AddCarter(c => c + .WithResponseNegotiator()); + +var app = builder.Build(); +app.MapCarter(); +app.Run(); +``` + +## Related Packages + +* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/PackageReleaseNotes.txt index 70d53eb..d5e4ce1 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/PackageReleaseNotes.txt @@ -1,59 +1,59 @@ -Version: 1.0.9 -Availability: .NET 10 +Version: 1.1.0 +Availability: .NET 11 and .NET 10 # ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) +- CHANGED Target frameworks by adding .NET 11 (net11.0) so the package now targets .NET 11 and .NET 10 Version: 1.0.8 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.7 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.6 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.5 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.4 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.3 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.2 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.1 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.0 -Availability: .NET 10 - -# New Features -- ADDED XmlResponseNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Xml namespace that provides an XML response negotiator for Carter, capable of serializing response models to XML format using System.Xml.XmlWriter +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.2 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.1 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.0 +Availability: .NET 10 + +# New Features +- ADDED XmlResponseNegotiator class in the Codebelt.Extensions.Carter.AspNetCore.Xml namespace that provides an XML response negotiator for Carter, capable of serializing response models to XML format using System.Xml.XmlWriter diff --git a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/README.md b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/README.md index bbbd999..58ee98c 100644 --- a/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/README.md +++ b/.nuget/Codebelt.Extensions.Carter.AspNetCore.Xml/README.md @@ -1,77 +1,79 @@ -# Codebelt.Extensions.Carter.AspNetCore.Xml - -An XML response negotiator for Carter in ASP.NET Core minimal APIs. - -## About - -**Codebelt.Extensions.Carter.AspNetCore.Xml** extends the **Codebelt.Extensions.Carter** package with a dedicated XML response negotiator for Carter, capable of serializing response models to XML format using System.Xml.XmlWriter. - -Use this package when you need XML-first integrations and want Carter endpoints to participate in explicit content negotiation. - -## CSharp Example - -Functional-test style sample (same bootstrapping pattern used by this repository): - -```csharp -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Carter.AspNetCore.Xml; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.AspNetCore.Xml; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalXmlOptions(o => - { - o.Settings.Writer.Indent = true; - }); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); - return await client.GetAsync("/world/statistical-regions"); - }); -``` - -Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): - -```csharp -using Carter; -using Codebelt.Extensions.Carter.AspNetCore.Xml; -using Cuemon.Extensions.AspNetCore.Xml; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddMinimalXmlOptions(); -builder.Services.AddCarter(c => c - .WithResponseNegotiator()); - -var app = builder.Build(); -app.MapCarter(); -app.Run(); -``` - -## Related Packages - -* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 +# Codebelt.Extensions.Carter.AspNetCore.Xml + +An XML response negotiator for Carter in ASP.NET Core minimal APIs. + +Target frameworks: .NET 10 (`net10.0`) and .NET 11 (`net11.0`). + +## About + +**Codebelt.Extensions.Carter.AspNetCore.Xml** extends the **Codebelt.Extensions.Carter** package with a dedicated XML response negotiator for Carter, capable of serializing response models to XML format using System.Xml.XmlWriter. + +Use this package when you need XML-first integrations and want Carter endpoints to participate in explicit content negotiation. + +## CSharp Example + +Functional-test style sample (same bootstrapping pattern used by this repository): + +```csharp +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Carter.AspNetCore.Xml; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.AspNetCore.Xml; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalXmlOptions(o => + { + o.Settings.Writer.Indent = true; + }); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); + return await client.GetAsync("/world/statistical-regions"); + }); +``` + +Program-style usage for production apps (remember to inherit from ICarterModule for your endpoints and add other services as needed): + +```csharp +using Carter; +using Codebelt.Extensions.Carter.AspNetCore.Xml; +using Cuemon.Extensions.AspNetCore.Xml; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddMinimalXmlOptions(); +builder.Services.AddCarter(c => c + .WithResponseNegotiator()); + +var app = builder.Build(); +app.MapCarter(); +app.Run(); +``` + +## Related Packages + +* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 diff --git a/.nuget/Codebelt.Extensions.Carter/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Carter/PackageReleaseNotes.txt index efe3c3e..6eb416e 100644 --- a/.nuget/Codebelt.Extensions.Carter/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Carter/PackageReleaseNotes.txt @@ -1,60 +1,66 @@ +Version: 1.1.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) so the package now targets .NET 11 and .NET 10 + Version: 1.0.9 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.8 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.7 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.6 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.5 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.4 -Availability: .NET 10 +Availability: .NET 10 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 1.0.3 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.2 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.1 -Availability: .NET 10 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 1.0.0 -Availability: .NET 10 - -# New Features -- ADDED ConfigurableResponseNegotiator class in the Codebelt.Extensions.Carter.Response namespace that provides an abstract, configurable base class for Carter response negotiators that serialize models using a StreamFormatter implementation -- ADDED EndpointConventionBuilderExtensions class in the Codebelt.Extensions.Carter namespace that consist of extension methods for the IEndpointConventionBuilder interface: Produces and Produces +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.2 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.1 +Availability: .NET 10 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 1.0.0 +Availability: .NET 10 + +# New Features +- ADDED ConfigurableResponseNegotiator class in the Codebelt.Extensions.Carter.Response namespace that provides an abstract, configurable base class for Carter response negotiators that serialize models using a StreamFormatter implementation +- ADDED EndpointConventionBuilderExtensions class in the Codebelt.Extensions.Carter namespace that consist of extension methods for the IEndpointConventionBuilder interface: Produces and Produces diff --git a/.nuget/Codebelt.Extensions.Carter/README.md b/.nuget/Codebelt.Extensions.Carter/README.md index e0f4e0e..07fc504 100644 --- a/.nuget/Codebelt.Extensions.Carter/README.md +++ b/.nuget/Codebelt.Extensions.Carter/README.md @@ -1,46 +1,48 @@ -# Codebelt.Extensions.Carter - -A focused extension layer for Carter with configurable response negotiation primitives and endpoint metadata helpers for ASP.NET Core minimal APIs. - -## About - -**Codebelt.Extensions.Carter** complements Carter by adding reusable negotiation infrastructure and endpoint convention extensions. - -Use this package when you want to: - -- Add explicit response metadata from Carter route mappings. -- Build on configurable negotiator abstractions. -- Keep minimal API modules expressive and OpenAPI-friendly. - -For concrete JSON, YAML, or XML negotiators, use one of the companion packages listed below. - -## CSharp Example - -```csharp -using Carter; -using Codebelt.Extensions.Carter; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; - -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddCarter(); - -var app = builder.Build(); - -app.MapGet("/status", () => new StatusResponse("ok")) - .Produces(StatusCodes.Status200OK, "application/json") - .ProducesProblem(StatusCodes.Status503ServiceUnavailable); - -app.MapCarter(); -app.Run(); - -public sealed record StatusResponse(string State); -``` - -## Related Packages - -* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 -* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 +# Codebelt.Extensions.Carter + +A focused extension layer for Carter with configurable response negotiation primitives and endpoint metadata helpers for ASP.NET Core minimal APIs. + +Target frameworks: .NET 10 (`net10.0`) and .NET 11 (`net11.0`). + +## About + +**Codebelt.Extensions.Carter** complements Carter by adding reusable negotiation infrastructure and endpoint convention extensions. + +Use this package when you want to: + +- Add explicit response metadata from Carter route mappings. +- Build on configurable negotiator abstractions. +- Keep minimal API modules expressive and OpenAPI-friendly. + +For concrete JSON, YAML, or XML negotiators, use one of the companion packages listed below. + +## CSharp Example + +```csharp +using Carter; +using Codebelt.Extensions.Carter; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddCarter(); + +var app = builder.Build(); + +app.MapGet("/status", () => new StatusResponse("ok")) + .Produces(StatusCodes.Status200OK, "application/json") + .ProducesProblem(StatusCodes.Status503ServiceUnavailable); + +app.MapCarter(); +app.Run(); + +public sealed record StatusResponse(string State); +``` + +## Related Packages + +* [Codebelt.Extensions.Carter](https://www.nuget.org/packages/Codebelt.Extensions.Carter/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Json](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Json/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Text.Yaml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/) 📦 +* [Codebelt.Extensions.Carter.AspNetCore.Xml](https://www.nuget.org/packages/Codebelt.Extensions.Carter.AspNetCore.Xml/) 📦 diff --git a/AGENTS.md b/AGENTS.md index 7965557..cc24482 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ This document provides guidance for AI agents working in this repository. ## Project Overview -Codebelt.Extensions.Carter is a suite of .NET libraries providing extensions and integrations for the Carter framework—a lightweight, composable REST API framework for ASP.NET Core. The solution targets .NET 10.0, .NET 9.0, and .NET Standard 2.0. It includes: +Codebelt.Extensions.Carter is a suite of .NET libraries providing extensions and integrations for the Carter framework—a lightweight, composable REST API framework for ASP.NET Core. The solution targets .NET 10.0 and .NET 11.0. It includes: - **Codebelt.Extensions.Carter** — Core extensions for Carter endpoint conventions and response handling. - **Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json** — Content negotiation using Newtonsoft.Json (JSON.NET). diff --git a/CHANGELOG.md b/CHANGELOG.md index d12fb4b..0142fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), For more details, please refer to `PackageReleaseNotes.txt` on a per assembly basis in the `.nuget` folder. +## [1.1.0] - 2026-11-10 + +This is a minor release that adds .NET 11 as a supported target framework alongside .NET 10 for all packages, with matching build, CI, and documentation updates. Source and test changes are formatting only, and the release also upgrades the Codebelt.Coverlet.MTP test coverage tooling. + +### Changed + +- Added .NET 11 (`net11.0`) as a target framework alongside .NET 10 for the Codebelt.Extensions.Carter packages, with shared build properties also targeting `net11.0` for the source, test, and benchmark projects, +- Updated "Codebelt.Coverlet.MTP" from 10.1.0 to 10.1.1, +- Documented .NET 11 support in the repository README, AGENTS guidance, contributing guide, availability include, and package READMEs, +- Moved CI runners to Ubuntu 26.04 and enabled preview SDKs so .NET 11 can be built and tested before its final release. + ## [1.0.9] - 2026-10-17 This is a patch release updating dependencies, documentation hosting, and repository agent guidance. @@ -162,6 +173,7 @@ This is the initial stable release of the `Codebelt.Extensions.Carter`, `Codebel - `YamlResponseNegotiator` class in the Codebelt.Extensions.Carter.AspNetCore.Text.Yaml namespace that provides a YAML response negotiator for Carter, capable of serializing response models to YAML format using `YamlDotNet`, - `XmlResponseNegotiator` class in the Codebelt.Extensions.Carter.AspNetCore.Xml namespace that provides an XML response negotiator for Carter, capable of serializing response models to XML format using `System.Xml.XmlWriter`. +[1.1.0]: https://github.com/codebeltnet/carter/compare/v1.0.9...v1.1.0 [1.0.9]: https://github.com/codebeltnet/carter/compare/v1.0.8...v1.0.9 [1.0.8]: https://github.com/codebeltnet/carter/compare/v1.0.7...v1.0.8 [1.0.7]: https://github.com/codebeltnet/carter/compare/v1.0.6...v1.0.7 diff --git a/Codebelt.Extensions.Carter.slnx b/Codebelt.Extensions.Carter.slnx index 83dda2f..84c3ab3 100644 --- a/Codebelt.Extensions.Carter.slnx +++ b/Codebelt.Extensions.Carter.slnx @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/Directory.Build.props b/Directory.Build.props index 3ac989c..ac85ac1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,113 +1,114 @@ - - - $(MSBuildProjectName.EndsWith('Tests')) - $(MSBuildProjectName.EndsWith('Benchmarks')) - $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src')) - $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling')) - $([MSBuild]::IsOSPlatform('Linux')) - $([MSBuild]::IsOSPlatform('Windows')) - true - false - ..\..\.nuget\$(MSBuildProjectName)\PackageReleaseNotes.txt - latest - - - - true - true - - - - net10.0 - Copyright © Geekle 2026. All rights reserved. - gimlichael - Geekle - Extensions for Carter API by Codebelt - icon.png - README.md - https://carter.codebelt.net/ - MIT - https://github.com/codebeltnet/carter - git - en-US - true - true - true - snupkg - true - true - $(MSBuildThisFileDirectory)carter.snk - true - latest - Recommended - 7035,CA2260,S6618 - v - true - - - - - - - - - - - - - - net10.0 - false - Exe - false - false - false - true - none - NU1701,NU1902,NU1903 - false - false - true - - - - 0 - - - - false - Exe - true - - - - - - - - - - - - - - - - - net10.0 - false - false - false - false - true - none - NU1701,NU1902,NU1903 - false - false - - - - - - - + + + $(MSBuildProjectName.EndsWith('Tests')) + $(MSBuildProjectName.EndsWith('Benchmarks')) + $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src')) + $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling')) + $([MSBuild]::IsOSPlatform('Linux')) + $([MSBuild]::IsOSPlatform('Windows')) + true + false + ..\..\.nuget\$(MSBuildProjectName)\PackageReleaseNotes.txt + latest + + + + true + true + + + + net11.0;net10.0 + Copyright © Geekle 2026. All rights reserved. + gimlichael + Geekle + Extensions for Carter API by Codebelt + icon.png + README.md + https://carter.codebelt.net/ + MIT + https://github.com/codebeltnet/carter + git + en-US + true + true + true + snupkg + true + true + $(MSBuildThisFileDirectory)carter.snk + true + latest + Recommended + 7035,CA2260,S6618 + v + true + + + + + + + + + + + + + + + net11.0;net10.0 + false + Exe + false + false + false + true + none + NU1701,NU1902,NU1903 + false + false + true + + + + 0 + + + + false + Exe + true + + + + + + + + + + + + + + + + + net11.0;net10.0 + false + false + false + false + true + none + NU1701,NU1902,NU1903 + false + false + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 5f9df1e..f6a6a00 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,14 +1,14 @@ - - - - $([System.IO.File]::ReadAllText('$(PackageReleaseNotesFile)')) - - - - - - 0 - $(MinVerMajor).$(MinVerMinor).$(MinVerPatch).$(GITHUB_RUN_NUMBER) - - - + + + + $([System.IO.File]::ReadAllText('$(PackageReleaseNotesFile)')) + + + + + + 0 + $(MinVerMajor).$(MinVerMinor).$(MinVerPatch).$(GITHUB_RUN_NUMBER) + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index e8c7f71..ecdcb96 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -14,11 +14,12 @@ - + + - \ No newline at end of file + diff --git a/README.md b/README.md index 1a86d02..9ac7df1 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An open-source project (MIT license) that targets and complements the [Carter](https://github.com/dotnet/Carter) API library. It provides a uniform and convenient way of doing API development for all project types in .NET. -Your versatile Carter companion for modern development with `.NET 10`. +Your versatile Carter companion for modern development with `.NET 10` and `.NET 11`. It is, by heart, free, flexible and built to extend and boost your agile codebelt. diff --git a/context7.json b/context7.json index 4b66380..5936e11 100644 --- a/context7.json +++ b/context7.json @@ -1,4 +1,4 @@ -{ - "url": "https://context7.com/codebeltnet/carter", - "public_key": "pk_ZYmd0ipMJCtW5NudkKPxA" -} +{ + "url": "https://context7.com/codebeltnet/carter", + "public_key": "pk_ZYmd0ipMJCtW5NudkKPxA" +} diff --git a/src/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.csproj b/src/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.csproj index dd741b0..570829d 100644 --- a/src/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.csproj +++ b/src/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.csproj @@ -1,14 +1,14 @@ - - - - The Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json namespace contains types that complements the Codebelt.Extensions.Carter namespace by providing an opinionated JSON response negotiator for Carter using Newtonsoft.Json in the context of ASP.NET Core. This is a Codebelt ecosystem alternative to Carter's built-in Newtonsoft.Json negotiator. - extension-methods extensions carter response-negotiation json newtonsoft-json asp-net-core minimal-api codebelt - - - - - - - - - + + + + The Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json namespace contains types that complements the Codebelt.Extensions.Carter namespace by providing an opinionated JSON response negotiator for Carter using Newtonsoft.Json in the context of ASP.NET Core. This is a Codebelt ecosystem alternative to Carter's built-in Newtonsoft.Json negotiator. + extension-methods extensions carter response-negotiation json newtonsoft-json asp-net-core minimal-api codebelt + + + + + + + + + diff --git a/src/Codebelt.Extensions.Carter.AspNetCore.Text.Json/Codebelt.Extensions.Carter.AspNetCore.Text.Json.csproj b/src/Codebelt.Extensions.Carter.AspNetCore.Text.Json/Codebelt.Extensions.Carter.AspNetCore.Text.Json.csproj index b6bfb89..62b0c9a 100644 --- a/src/Codebelt.Extensions.Carter.AspNetCore.Text.Json/Codebelt.Extensions.Carter.AspNetCore.Text.Json.csproj +++ b/src/Codebelt.Extensions.Carter.AspNetCore.Text.Json/Codebelt.Extensions.Carter.AspNetCore.Text.Json.csproj @@ -1,14 +1,14 @@ - - - - The Codebelt.Extensions.Carter.AspNetCore.Text.Json namespace contains types that complements the Codebelt.Extensions.Carter namespace by providing an opinionated JSON response negotiator for Carter using System.Text.Json in the context of ASP.NET Core. This is a Codebelt ecosystem alternative to Carter's built-in JSON negotiator. - extension-methods extensions carter response-negotiation json system-text-json asp-net-core minimal-api codebelt - - - - - - - - - + + + + The Codebelt.Extensions.Carter.AspNetCore.Text.Json namespace contains types that complements the Codebelt.Extensions.Carter namespace by providing an opinionated JSON response negotiator for Carter using System.Text.Json in the context of ASP.NET Core. This is a Codebelt ecosystem alternative to Carter's built-in JSON negotiator. + extension-methods extensions carter response-negotiation json system-text-json asp-net-core minimal-api codebelt + + + + + + + + + diff --git a/src/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/YamlResponseNegotiator.cs b/src/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/YamlResponseNegotiator.cs index 63a4087..29a2558 100644 --- a/src/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/YamlResponseNegotiator.cs +++ b/src/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml/YamlResponseNegotiator.cs @@ -1,37 +1,37 @@ -using Codebelt.Extensions.YamlDotNet.Formatters; -using Microsoft.Extensions.Options; -using System.Text; -using Codebelt.Extensions.Carter.Response; -using Cuemon.Runtime.Serialization.Formatters; - -namespace Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; - -/// -/// Provides a YAML response negotiator for Carter, capable of serializing response models to YAML format. -/// -/// -public class YamlResponseNegotiator : ConfigurableResponseNegotiator -{ - /// - /// Initializes a new instance of the class. - /// - /// The used to configure YAML serialization and supported media types. - public YamlResponseNegotiator(IOptions options) : base(options.Value) - { - } - - /// - /// Returns the character encoding specified by the . - /// - /// The default for this negotiator. - protected override Encoding GetDefaultEncoding() => Options.Encoding; - - /// - /// Returns a new configured with the current . - /// - /// A instance configured with the current . - public override StreamFormatter GetFormatter() - { - return new YamlFormatter(Options); - } -} +using Codebelt.Extensions.YamlDotNet.Formatters; +using Microsoft.Extensions.Options; +using System.Text; +using Codebelt.Extensions.Carter.Response; +using Cuemon.Runtime.Serialization.Formatters; + +namespace Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; + +/// +/// Provides a YAML response negotiator for Carter, capable of serializing response models to YAML format. +/// +/// +public class YamlResponseNegotiator : ConfigurableResponseNegotiator +{ + /// + /// Initializes a new instance of the class. + /// + /// The used to configure YAML serialization and supported media types. + public YamlResponseNegotiator(IOptions options) : base(options.Value) + { + } + + /// + /// Returns the character encoding specified by the . + /// + /// The default for this negotiator. + protected override Encoding GetDefaultEncoding() => Options.Encoding; + + /// + /// Returns a new configured with the current . + /// + /// A instance configured with the current . + public override StreamFormatter GetFormatter() + { + return new YamlFormatter(Options); + } +} diff --git a/src/Codebelt.Extensions.Carter.AspNetCore.Xml/XmlResponseNegotiator.cs b/src/Codebelt.Extensions.Carter.AspNetCore.Xml/XmlResponseNegotiator.cs index 8fe09dc..4ee5be5 100644 --- a/src/Codebelt.Extensions.Carter.AspNetCore.Xml/XmlResponseNegotiator.cs +++ b/src/Codebelt.Extensions.Carter.AspNetCore.Xml/XmlResponseNegotiator.cs @@ -10,7 +10,7 @@ namespace Codebelt.Extensions.Carter.AspNetCore.Xml; /// Provides an XML response negotiator for Carter, capable of serializing response models to XML format. /// /// - public class XmlResponseNegotiator : ConfigurableResponseNegotiator +public class XmlResponseNegotiator : ConfigurableResponseNegotiator { /// /// Initializes a new instance of the class. diff --git a/src/Codebelt.Extensions.Carter/Codebelt.Extensions.Carter.csproj b/src/Codebelt.Extensions.Carter/Codebelt.Extensions.Carter.csproj index e7ac12d..3e3f6ca 100644 --- a/src/Codebelt.Extensions.Carter/Codebelt.Extensions.Carter.csproj +++ b/src/Codebelt.Extensions.Carter/Codebelt.Extensions.Carter.csproj @@ -1,13 +1,13 @@ - - - - The Codebelt.Extensions.Carter namespace contains types and extension methods that complements the Carter namespace by providing configurable response negotiation, endpoint convention builder extensions and content negotiation for ASP.NET Core minimal APIs. - extension-methods extensions carter response-negotiation content-negotiation asp-net-core minimal-api - - - - - - - - + + + + The Codebelt.Extensions.Carter namespace contains types and extension methods that complements the Carter namespace by providing configurable response negotiation, endpoint convention builder extensions and content negotiation for ASP.NET Core minimal APIs. + extension-methods extensions carter response-negotiation content-negotiation asp-net-core minimal-api + + + + + + + + diff --git a/src/Codebelt.Extensions.Carter/EndpointConventionBuilderExtensions.cs b/src/Codebelt.Extensions.Carter/EndpointConventionBuilderExtensions.cs index 5def504..655c5ba 100644 --- a/src/Codebelt.Extensions.Carter/EndpointConventionBuilderExtensions.cs +++ b/src/Codebelt.Extensions.Carter/EndpointConventionBuilderExtensions.cs @@ -1,36 +1,36 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; - -namespace Codebelt.Extensions.Carter; - -/// -/// Extension methods for . -/// -public static class EndpointConventionBuilderExtensions -{ - /// The to add the metadata to. - extension(IEndpointConventionBuilder builder) - { - /// - /// Adds metadata indicating that the endpoint produces a response of the specified type with the given status code and content types. - /// - /// The type of the response body. - /// The HTTP status code of the response. Defaults to . - /// The content types produced by the endpoint. - /// The with the added metadata. - public IEndpointConventionBuilder Produces(int statusCode = StatusCodes.Status200OK, params string[] contentTypes) - { - return builder.WithMetadata(new ProducesResponseTypeMetadata(statusCode, typeof(TResponse), contentTypes)); - } - - /// - /// Adds metadata indicating that the endpoint produces a response with the given status code and no response body. - /// - /// The HTTP status code of the response. - /// The with the added metadata. - public IEndpointConventionBuilder Produces(int statusCode) - { - return builder.WithMetadata(new ProducesResponseTypeMetadata(statusCode)); - } - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace Codebelt.Extensions.Carter; + +/// +/// Extension methods for . +/// +public static class EndpointConventionBuilderExtensions +{ + /// The to add the metadata to. + extension(IEndpointConventionBuilder builder) + { + /// + /// Adds metadata indicating that the endpoint produces a response of the specified type with the given status code and content types. + /// + /// The type of the response body. + /// The HTTP status code of the response. Defaults to . + /// The content types produced by the endpoint. + /// The with the added metadata. + public IEndpointConventionBuilder Produces(int statusCode = StatusCodes.Status200OK, params string[] contentTypes) + { + return builder.WithMetadata(new ProducesResponseTypeMetadata(statusCode, typeof(TResponse), contentTypes)); + } + + /// + /// Adds metadata indicating that the endpoint produces a response with the given status code and no response body. + /// + /// The HTTP status code of the response. + /// The with the added metadata. + public IEndpointConventionBuilder Produces(int statusCode) + { + return builder.WithMetadata(new ProducesResponseTypeMetadata(statusCode)); + } + } +} diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests.csproj b/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests.csproj index f41d9e9..d183ca3 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests.csproj +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests.csproj @@ -1,16 +1,16 @@ - - - - Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json - - - - - - - - - - - + + + + Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/NewtonsoftJsonNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/NewtonsoftJsonNegotiatorTest.cs index 9b96867..ae5f9b5 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/NewtonsoftJsonNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json.Tests/NewtonsoftJsonNegotiatorTest.cs @@ -1,98 +1,98 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Carter; -using Carter.Response; -using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; -using Codebelt.Extensions.Newtonsoft.Json.Formatters; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Options; -using Microsoft.AspNetCore.Http; -using Xunit; -using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; - -namespace Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; - -/// -/// Tests for the class. -/// -public class NewtonsoftJsonNegotiatorTest : Test -{ - public NewtonsoftJsonNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Theory] - [InlineData("application/json")] - [InlineData("*/*")] - public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) - { - var sut = new NewtonsoftJsonNegotiator(Options.Create(new NewtonsoftJsonFormatterOptions())); - - Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Theory] - [InlineData("application/xml")] - [InlineData("text/xml")] - [InlineData("application/yaml")] - public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) - { - var sut = new NewtonsoftJsonNegotiator(Options.Create(new NewtonsoftJsonFormatterOptions - { - SupportedMediaTypes = new List - { - new("application/json") - } - })); - - Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Fact] - public async Task Handle_ShouldWriteJsonToResponseBody_WithCorrectContentType() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddNewtonsoftJsonFormatterOptions(); - services.AddCarter(configurator: c => c.WithResponseNegotiator()); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => - res.Negotiate(new FakeModel { Id = 1, Name = "Newtonsoft" }, ct)); - }); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - return await client.GetAsync("/"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - Assert.Contains("1", body); - Assert.Contains("Newtonsoft", body); - - TestOutput.WriteLine(body); - } -} - -internal sealed class FakeModel -{ - public int Id { get; init; } - public string Name { get; init; } -} +using System.Collections.Generic; +using System.Net; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Carter; +using Carter.Response; +using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; +using Codebelt.Extensions.Newtonsoft.Json.Formatters; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Http; +using Xunit; +using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; + +namespace Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; + +/// +/// Tests for the class. +/// +public class NewtonsoftJsonNegotiatorTest : Test +{ + public NewtonsoftJsonNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData("application/json")] + [InlineData("*/*")] + public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) + { + var sut = new NewtonsoftJsonNegotiator(Options.Create(new NewtonsoftJsonFormatterOptions())); + + Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Theory] + [InlineData("application/xml")] + [InlineData("text/xml")] + [InlineData("application/yaml")] + public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) + { + var sut = new NewtonsoftJsonNegotiator(Options.Create(new NewtonsoftJsonFormatterOptions + { + SupportedMediaTypes = new List + { + new("application/json") + } + })); + + Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Fact] + public async Task Handle_ShouldWriteJsonToResponseBody_WithCorrectContentType() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddNewtonsoftJsonFormatterOptions(); + services.AddCarter(configurator: c => c.WithResponseNegotiator()); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => + res.Negotiate(new FakeModel { Id = 1, Name = "Newtonsoft" }, ct)); + }); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return await client.GetAsync("/"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + Assert.Contains("1", body); + Assert.Contains("Newtonsoft", body); + + TestOutput.WriteLine(body); + } +} + +internal sealed class FakeModel +{ + public int Id { get; init; } + public string Name { get; init; } +} diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests.csproj b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests.csproj index c72f3b2..d73d900 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests.csproj +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests.csproj @@ -1,16 +1,16 @@ - - - - Codebelt.Extensions.Carter.AspNetCore.Text.Json - - - - - - - - - - - + + + + Codebelt.Extensions.Carter.AspNetCore.Text.Json + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/JsonResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/JsonResponseNegotiatorTest.cs index 9d97860..88fcca8 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/JsonResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Json.Tests/JsonResponseNegotiatorTest.cs @@ -1,98 +1,98 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Carter; -using Carter.Response; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.Text.Json.Formatters; -using Microsoft.Extensions.Options; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Xunit; -using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; - -namespace Codebelt.Extensions.Carter.AspNetCore.Text.Json; - -/// -/// Tests for the class. -/// -public class JsonResponseNegotiatorTest : Test -{ - public JsonResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Theory] - [InlineData("application/json")] - [InlineData("*/*")] - public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) - { - var sut = new JsonResponseNegotiator(Options.Create(new JsonFormatterOptions())); - - Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Theory] - [InlineData("application/xml")] - [InlineData("text/xml")] - [InlineData("application/yaml")] - public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) - { - var sut = new JsonResponseNegotiator(Options.Create(new JsonFormatterOptions - { - SupportedMediaTypes = new List - { - new("application/json") - } - })); - - Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Fact] - public async Task Handle_ShouldWriteJsonToResponseBody_WithCorrectContentType() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddSingleton(new JsonFormatterOptions()); - services.AddCarter(configurator: c => c.WithResponseNegotiator()); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => - res.Negotiate(new FakeModel { Id = 1, Name = "Json" }, ct)); - }); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - return await client.GetAsync("/"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - Assert.Contains("1", body); - Assert.Contains("Json", body); - - TestOutput.WriteLine(body); - } -} - -internal sealed class FakeModel -{ - public int Id { get; init; } - public string Name { get; init; } -} +using System.Collections.Generic; +using System.Net; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Carter; +using Carter.Response; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Text.Json.Formatters; +using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; + +namespace Codebelt.Extensions.Carter.AspNetCore.Text.Json; + +/// +/// Tests for the class. +/// +public class JsonResponseNegotiatorTest : Test +{ + public JsonResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData("application/json")] + [InlineData("*/*")] + public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) + { + var sut = new JsonResponseNegotiator(Options.Create(new JsonFormatterOptions())); + + Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Theory] + [InlineData("application/xml")] + [InlineData("text/xml")] + [InlineData("application/yaml")] + public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) + { + var sut = new JsonResponseNegotiator(Options.Create(new JsonFormatterOptions + { + SupportedMediaTypes = new List + { + new("application/json") + } + })); + + Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Fact] + public async Task Handle_ShouldWriteJsonToResponseBody_WithCorrectContentType() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddSingleton(new JsonFormatterOptions()); + services.AddCarter(configurator: c => c.WithResponseNegotiator()); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => + res.Negotiate(new FakeModel { Id = 1, Name = "Json" }, ct)); + }); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return await client.GetAsync("/"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + Assert.Contains("1", body); + Assert.Contains("Json", body); + + TestOutput.WriteLine(body); + } +} + +internal sealed class FakeModel +{ + public int Id { get; init; } + public string Name { get; init; } +} diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests.csproj b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests.csproj index cc0e523..27a6361 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests.csproj +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests.csproj @@ -1,16 +1,16 @@ - - - - Codebelt.Extensions.Carter.AspNetCore.Text.Yaml - - - - - - - - - - - - + + + + Codebelt.Extensions.Carter.AspNetCore.Text.Yaml + + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/YamlResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/YamlResponseNegotiatorTest.cs index 91f0630..f47241f 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/YamlResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Text.Yaml.Tests/YamlResponseNegotiatorTest.cs @@ -1,102 +1,102 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Carter; -using Carter.Response; -using Codebelt.Extensions.AspNetCore.Text.Yaml; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Codebelt.Extensions.YamlDotNet.Formatters; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; -using Xunit; -using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; - -namespace Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; - -/// -/// Tests for the class. -/// -public class YamlResponseNegotiatorTest : Test -{ - public YamlResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Theory] - [InlineData("application/yaml")] - [InlineData("text/yaml")] - [InlineData("text/plain")] - [InlineData("*/*")] - public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) - { - var sut = new YamlResponseNegotiator(Options.Create(new YamlFormatterOptions())); - - Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Theory] - [InlineData("application/json")] - [InlineData("text/xml")] - [InlineData("application/xml")] - public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) - { - var sut = new YamlResponseNegotiator(Options.Create(new YamlFormatterOptions - { - SupportedMediaTypes = new List - { - new("application/x-yaml"), - new("application/yaml"), - new("text/yaml") - } - })); - - Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Fact] - public async Task Handle_ShouldWriteYamlToResponseBody_WithCorrectContentType() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalYamlOptions(); - services.AddCarter(configurator: c => c.WithResponseNegotiator()); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => - res.Negotiate(new FakeModel { Id = 1, Name = "YAML" }, ct)); - }); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/yaml")); - return await client.GetAsync("/"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/yaml", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - Assert.Contains("id: 1", body); - Assert.Contains("name: YAML", body); - - TestOutput.WriteLine(body); - } -} - -internal sealed class FakeModel -{ - public int Id { get; init; } - public string Name { get; init; } -} +using System.Collections.Generic; +using System.Net; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Carter; +using Carter.Response; +using Codebelt.Extensions.AspNetCore.Text.Yaml; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Codebelt.Extensions.YamlDotNet.Formatters; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Xunit; +using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; + +namespace Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; + +/// +/// Tests for the class. +/// +public class YamlResponseNegotiatorTest : Test +{ + public YamlResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData("application/yaml")] + [InlineData("text/yaml")] + [InlineData("text/plain")] + [InlineData("*/*")] + public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) + { + var sut = new YamlResponseNegotiator(Options.Create(new YamlFormatterOptions())); + + Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Theory] + [InlineData("application/json")] + [InlineData("text/xml")] + [InlineData("application/xml")] + public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) + { + var sut = new YamlResponseNegotiator(Options.Create(new YamlFormatterOptions + { + SupportedMediaTypes = new List + { + new("application/x-yaml"), + new("application/yaml"), + new("text/yaml") + } + })); + + Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Fact] + public async Task Handle_ShouldWriteYamlToResponseBody_WithCorrectContentType() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalYamlOptions(); + services.AddCarter(configurator: c => c.WithResponseNegotiator()); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => + res.Negotiate(new FakeModel { Id = 1, Name = "YAML" }, ct)); + }); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/yaml")); + return await client.GetAsync("/"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/yaml", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + Assert.Contains("id: 1", body); + Assert.Contains("name: YAML", body); + + TestOutput.WriteLine(body); + } +} + +internal sealed class FakeModel +{ + public int Id { get; init; } + public string Name { get; init; } +} diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests.csproj b/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests.csproj index dd2b46c..dd5557d 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests.csproj +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests.csproj @@ -1,16 +1,16 @@ - - - - Codebelt.Extensions.Carter.AspNetCore.Xml - - - - - - - - - - - - + + + + Codebelt.Extensions.Carter.AspNetCore.Xml + + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/XmlResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/XmlResponseNegotiatorTest.cs index b4ff25f..4f84b39 100644 --- a/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/XmlResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.AspNetCore.Xml.Tests/XmlResponseNegotiatorTest.cs @@ -1,100 +1,100 @@ -using System.Collections.Generic; -using System.Net; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Carter; -using Carter.Response; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.AspNetCore.Xml; -using Cuemon.Xml.Serialization.Formatters; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; -using Xunit; -using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; - -namespace Codebelt.Extensions.Carter.AspNetCore.Xml; - -/// -/// Tests for the class. -/// -public class XmlResponseNegotiatorTest : Test -{ - public XmlResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Theory] - [InlineData("application/xml")] - [InlineData("text/xml")] - [InlineData("*/*")] - public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) - { - var sut = new XmlResponseNegotiator(Options.Create(new XmlFormatterOptions())); - - Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Theory] - [InlineData("application/json")] - [InlineData("text/yaml")] - [InlineData("application/yaml")] - public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) - { - var sut = new XmlResponseNegotiator(Options.Create(new XmlFormatterOptions - { - SupportedMediaTypes = new List - { - new("application/xml"), - new("text/xml") - } - })); - - Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); - } - - [Fact] - public async Task Handle_ShouldWriteXmlToResponseBody_WithCorrectContentType() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalXmlOptions(); - services.AddCarter(configurator: c => c.WithResponseNegotiator()); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => - res.Negotiate(new FakeModel { Id = 1, Name = "XML" }, ct)); - }); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); - return await client.GetAsync("/"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/xml", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - Assert.Contains("1", body); - Assert.Contains("XML", body); - - TestOutput.WriteLine(body); - } -} - -internal sealed class FakeModel -{ - public int Id { get; init; } - public string Name { get; init; } -} +using System.Collections.Generic; +using System.Net; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Carter; +using Carter.Response; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.AspNetCore.Xml; +using Cuemon.Xml.Serialization.Formatters; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Xunit; +using AspNetCoreMediaType = Microsoft.Net.Http.Headers.MediaTypeHeaderValue; + +namespace Codebelt.Extensions.Carter.AspNetCore.Xml; + +/// +/// Tests for the class. +/// +public class XmlResponseNegotiatorTest : Test +{ + public XmlResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData("application/xml")] + [InlineData("text/xml")] + [InlineData("*/*")] + public void CanHandle_ShouldReturnTrue_WhenMediaTypeIsSupported(string mediaType) + { + var sut = new XmlResponseNegotiator(Options.Create(new XmlFormatterOptions())); + + Assert.True(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Theory] + [InlineData("application/json")] + [InlineData("text/yaml")] + [InlineData("application/yaml")] + public void CanHandle_ShouldReturnFalse_WhenMediaTypeIsNotSupported(string mediaType) + { + var sut = new XmlResponseNegotiator(Options.Create(new XmlFormatterOptions + { + SupportedMediaTypes = new List + { + new("application/xml"), + new("text/xml") + } + })); + + Assert.False(sut.CanHandle(AspNetCoreMediaType.Parse(mediaType))); + } + + [Fact] + public async Task Handle_ShouldWriteXmlToResponseBody_WithCorrectContentType() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalXmlOptions(); + services.AddCarter(configurator: c => c.WithResponseNegotiator()); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", (HttpResponse res, CancellationToken ct) => + res.Negotiate(new FakeModel { Id = 1, Name = "XML" }, ct)); + }); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); + return await client.GetAsync("/"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/xml", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + Assert.Contains("1", body); + Assert.Contains("XML", body); + + TestOutput.WriteLine(body); + } +} + +internal sealed class FakeModel +{ + public int Id { get; init; } + public string Name { get; init; } +} diff --git a/test/Codebelt.Extensions.Carter.FunctionalTests/Assets/WorldModule.cs b/test/Codebelt.Extensions.Carter.FunctionalTests/Assets/WorldModule.cs index 7a2733c..2119c71 100644 --- a/test/Codebelt.Extensions.Carter.FunctionalTests/Assets/WorldModule.cs +++ b/test/Codebelt.Extensions.Carter.FunctionalTests/Assets/WorldModule.cs @@ -1,28 +1,28 @@ -using System.Linq; -using System.Threading; -using Carter; -using Carter.Response; -using Cuemon.Globalization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace Codebelt.Extensions.Carter.Assets; - -internal sealed class WorldModule : ICarterModule -{ - public void AddRoutes(IEndpointRouteBuilder app) - { - app.MapGet("/world/statistical-regions", (HttpResponse res, CancellationToken ct) => - res.Negotiate(World.StatisticalRegions - .Where(r => r.Kind == StatisticalRegionKind.World) - .Select(r => new StatisticalRegionModel { Code = r.Code, Name = r.Name, Kind = r.Kind }), ct)); - } -} - -internal sealed class StatisticalRegionModel -{ - public string Code { get; init; } - public string Name { get; init; } - public StatisticalRegionKind Kind { get; init; } -} +using System.Linq; +using System.Threading; +using Carter; +using Carter.Response; +using Cuemon.Globalization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Codebelt.Extensions.Carter.Assets; + +internal sealed class WorldModule : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + app.MapGet("/world/statistical-regions", (HttpResponse res, CancellationToken ct) => + res.Negotiate(World.StatisticalRegions + .Where(r => r.Kind == StatisticalRegionKind.World) + .Select(r => new StatisticalRegionModel { Code = r.Code, Name = r.Name, Kind = r.Kind }), ct)); + } +} + +internal sealed class StatisticalRegionModel +{ + public string Code { get; init; } + public string Name { get; init; } + public StatisticalRegionKind Kind { get; init; } +} diff --git a/test/Codebelt.Extensions.Carter.FunctionalTests/Codebelt.Extensions.Carter.FunctionalTests.csproj b/test/Codebelt.Extensions.Carter.FunctionalTests/Codebelt.Extensions.Carter.FunctionalTests.csproj index 8e38256..2c542d7 100644 --- a/test/Codebelt.Extensions.Carter.FunctionalTests/Codebelt.Extensions.Carter.FunctionalTests.csproj +++ b/test/Codebelt.Extensions.Carter.FunctionalTests/Codebelt.Extensions.Carter.FunctionalTests.csproj @@ -1,27 +1,27 @@ - - - - Codebelt.Extensions.Carter - - - - - - - - - - - - - - - - - - - - - - - + + + + Codebelt.Extensions.Carter + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Carter.FunctionalTests/JsonResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.FunctionalTests/JsonResponseNegotiatorTest.cs index 66d55b1..3a38a08 100644 --- a/test/Codebelt.Extensions.Carter.FunctionalTests/JsonResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.FunctionalTests/JsonResponseNegotiatorTest.cs @@ -1,32 +1,32 @@ -using System.Net; -using System.Net.Http.Headers; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.Carter.AspNetCore.Text.Json; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.AspNetCore.Text.Json; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Codebelt.Extensions.Carter; - -/// -/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. -/// -public class JsonResponseNegotiatorTest : Test -{ - public JsonResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task GetStatisticalRegions_ShouldReturnJsonResponse_WhenCarterDefaultsAreDisabled() - { - using var response = await MinimalWebHostTestFactory.RunAsync( +using System.Net; +using System.Net.Http.Headers; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.Carter.AspNetCore.Text.Json; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.AspNetCore.Text.Json; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Codebelt.Extensions.Carter; + +/// +/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. +/// +public class JsonResponseNegotiatorTest : Test +{ + public JsonResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task GetStatisticalRegions_ShouldReturnJsonResponse_WhenCarterDefaultsAreDisabled() + { + using var response = await MinimalWebHostTestFactory.RunAsync( services => { services.AddMinimalJsonOptions(o => @@ -37,26 +37,26 @@ public async Task GetStatisticalRegions_ShouldReturnJsonResponse_WhenCarterDefau .WithModule() .WithResponseNegotiator()); services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - return await client.GetAsync("/world/statistical-regions"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - - TestOutput.WriteLine(body); - } + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return await client.GetAsync("/world/statistical-regions"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + + TestOutput.WriteLine(body); + } } diff --git a/test/Codebelt.Extensions.Carter.FunctionalTests/NewtonsoftJsonNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.FunctionalTests/NewtonsoftJsonNegotiatorTest.cs index b129372..3d11fd8 100644 --- a/test/Codebelt.Extensions.Carter.FunctionalTests/NewtonsoftJsonNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.FunctionalTests/NewtonsoftJsonNegotiatorTest.cs @@ -1,63 +1,63 @@ -using System.Net; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; -using Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; -using Xunit; - -namespace Codebelt.Extensions.Carter; - -/// -/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. -/// -public class NewtonsoftJsonNegotiatorTest : Test -{ - public NewtonsoftJsonNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task GetStatisticalRegions_ShouldReturnJsonResponse_WhenCarterDefaultsAreDisabled() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddNewtonsoftJsonFormatterOptions(o => - { - o.Settings.Converters.Insert(0, new StringEnumConverter(new DefaultNamingStrategy(), false)); - }); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - return await client.GetAsync("/world/statistical-regions"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - - TestOutput.WriteLine(body); - } -} +using System.Net; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Formatters; +using Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Codebelt.Extensions.Carter; + +/// +/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. +/// +public class NewtonsoftJsonNegotiatorTest : Test +{ + public NewtonsoftJsonNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task GetStatisticalRegions_ShouldReturnJsonResponse_WhenCarterDefaultsAreDisabled() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddNewtonsoftJsonFormatterOptions(o => + { + o.Settings.Converters.Insert(0, new StringEnumConverter(new DefaultNamingStrategy(), false)); + }); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + return await client.GetAsync("/world/statistical-regions"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/json", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + + TestOutput.WriteLine(body); + } +} diff --git a/test/Codebelt.Extensions.Carter.FunctionalTests/XmlResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.FunctionalTests/XmlResponseNegotiatorTest.cs index cec3a78..f8166ef 100644 --- a/test/Codebelt.Extensions.Carter.FunctionalTests/XmlResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.FunctionalTests/XmlResponseNegotiatorTest.cs @@ -1,61 +1,61 @@ -using System.Net; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.Carter.AspNetCore.Xml; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Cuemon.Extensions.AspNetCore.Xml; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Codebelt.Extensions.Carter; - -/// -/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. -/// -public class XmlResponseNegotiatorTest : Test -{ - public XmlResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task GetStatisticalRegions_ShouldReturnXmlResponse_WhenCarterDefaultsAreDisabled() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalXmlOptions(o => - { - o.Settings.Writer.Indent = true; - }); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); - return await client.GetAsync("/world/statistical-regions"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/xml", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - - TestOutput.WriteLine(body); - } -} +using System.Net; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.Carter.AspNetCore.Xml; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.AspNetCore.Xml; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Codebelt.Extensions.Carter; + +/// +/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. +/// +public class XmlResponseNegotiatorTest : Test +{ + public XmlResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task GetStatisticalRegions_ShouldReturnXmlResponse_WhenCarterDefaultsAreDisabled() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalXmlOptions(o => + { + o.Settings.Writer.Indent = true; + }); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); + return await client.GetAsync("/world/statistical-regions"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/xml", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + + TestOutput.WriteLine(body); + } +} diff --git a/test/Codebelt.Extensions.Carter.FunctionalTests/YamlResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.FunctionalTests/YamlResponseNegotiatorTest.cs index a514406..fd5a3c7 100644 --- a/test/Codebelt.Extensions.Carter.FunctionalTests/YamlResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.FunctionalTests/YamlResponseNegotiatorTest.cs @@ -1,58 +1,58 @@ -using System.Net; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Carter; -using Codebelt.Extensions.AspNetCore.Text.Yaml; -using Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; -using Codebelt.Extensions.Carter.Assets; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Codebelt.Extensions.Carter; - -/// -/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. -/// -public class YamlResponseNegotiatorTest : Test -{ - public YamlResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public async Task GetStatisticalRegions_ShouldReturnYamlResponse_WhenCarterDefaultsAreDisabled() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddMinimalYamlOptions(); - services.AddCarter(configurator: c => c - .WithModule() - .WithResponseNegotiator()); - services.AddRouting(); - }, - app => - { - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapCarter()); - }, - _ => { }, - async client => - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/yaml")); - return await client.GetAsync("/world/statistical-regions"); - }); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.StartsWith("application/yaml", response.Content.Headers.ContentType?.ToString()); - - var body = await response.Content.ReadAsStringAsync(); - - Assert.NotEmpty(body); - - TestOutput.WriteLine(body); - } -} +using System.Net; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Carter; +using Codebelt.Extensions.AspNetCore.Text.Yaml; +using Codebelt.Extensions.Carter.AspNetCore.Text.Yaml; +using Codebelt.Extensions.Carter.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Codebelt.Extensions.Carter; + +/// +/// Functional tests verifying Carter bootstrapped with as the sole response negotiator. +/// +public class YamlResponseNegotiatorTest : Test +{ + public YamlResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task GetStatisticalRegions_ShouldReturnYamlResponse_WhenCarterDefaultsAreDisabled() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddMinimalYamlOptions(); + services.AddCarter(configurator: c => c + .WithModule() + .WithResponseNegotiator()); + services.AddRouting(); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => endpoints.MapCarter()); + }, + _ => { }, + async client => + { + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/yaml")); + return await client.GetAsync("/world/statistical-regions"); + }); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("application/yaml", response.Content.Headers.ContentType?.ToString()); + + var body = await response.Content.ReadAsStringAsync(); + + Assert.NotEmpty(body); + + TestOutput.WriteLine(body); + } +} diff --git a/test/Codebelt.Extensions.Carter.Tests/Codebelt.Extensions.Carter.Tests.csproj b/test/Codebelt.Extensions.Carter.Tests/Codebelt.Extensions.Carter.Tests.csproj index e85ffa0..d183cf8 100644 --- a/test/Codebelt.Extensions.Carter.Tests/Codebelt.Extensions.Carter.Tests.csproj +++ b/test/Codebelt.Extensions.Carter.Tests/Codebelt.Extensions.Carter.Tests.csproj @@ -1,15 +1,15 @@ - - - - Codebelt.Extensions.Carter - - - - - - - - - - - + + + + Codebelt.Extensions.Carter + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Carter.Tests/EndpointConventionBuilderExtensionsTest.cs b/test/Codebelt.Extensions.Carter.Tests/EndpointConventionBuilderExtensionsTest.cs index dadc671..7cc65e6 100644 --- a/test/Codebelt.Extensions.Carter.Tests/EndpointConventionBuilderExtensionsTest.cs +++ b/test/Codebelt.Extensions.Carter.Tests/EndpointConventionBuilderExtensionsTest.cs @@ -1,98 +1,97 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Codebelt.Extensions.Xunit; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Metadata; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Patterns; -using Xunit; - -namespace Codebelt.Extensions.Carter -{ - /// - /// Tests for the class. - /// - public class EndpointConventionBuilderExtensionsTest : Test - { - public EndpointConventionBuilderExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Produces_WithTResponse_ShouldAddProducesResponseTypeMetadata() - { - var delegateEndpointBuilder = new FakeEndpointConventionBuilder(); - - delegateEndpointBuilder.Produces(StatusCodes.Status201Created, "application/json", "text/plain"); - - var endpointBuilder = new RouteEndpointBuilder(context => System.Threading.Tasks.Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - foreach (var convention in delegateEndpointBuilder.Conventions) - { - convention(endpointBuilder); - } - - var typeMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault(); - - Assert.NotNull(typeMetadata); - Assert.Equal(StatusCodes.Status201Created, typeMetadata.StatusCode); - Assert.Equal(typeof(string), typeMetadata.Type); - Assert.Contains("application/json", typeMetadata.ContentTypes); - Assert.Contains("text/plain", typeMetadata.ContentTypes); - } - - [Fact] - public void Produces_WithTResponse_DefaultStatusCode_ShouldAddProducesResponseTypeMetadata() - { - var delegateEndpointBuilder = new FakeEndpointConventionBuilder(); - - delegateEndpointBuilder.Produces(contentTypes: "application/xml"); - - var endpointBuilder = new RouteEndpointBuilder(context => System.Threading.Tasks.Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - foreach (var convention in delegateEndpointBuilder.Conventions) - { - convention(endpointBuilder); - } - - var typeMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault(); - - Assert.NotNull(typeMetadata); - Assert.Equal(StatusCodes.Status200OK, typeMetadata.StatusCode); - Assert.Equal(typeof(int), typeMetadata.Type); - Assert.Contains("application/xml", typeMetadata.ContentTypes); - } - - [Fact] - public void Produces_WithStatusCode_ShouldAddProducesResponseTypeMetadata() - { - var delegateEndpointBuilder = new FakeEndpointConventionBuilder(); - - delegateEndpointBuilder.Produces(StatusCodes.Status404NotFound); - - var endpointBuilder = new RouteEndpointBuilder(context => System.Threading.Tasks.Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - foreach (var convention in delegateEndpointBuilder.Conventions) - { - convention(endpointBuilder); - } - - var typeMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault(); - - Assert.NotNull(typeMetadata); - Assert.Equal(StatusCodes.Status404NotFound, typeMetadata.StatusCode); - Assert.Null(typeMetadata.Type); - // ContentTypes might be empty - Assert.Empty(typeMetadata.ContentTypes); - } - - private class FakeEndpointConventionBuilder : IEndpointConventionBuilder - { - public List> Conventions { get; } = []; - - public void Add(Action convention) - { - Conventions.Add(convention); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Codebelt.Extensions.Xunit; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Metadata; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Patterns; +using Xunit; + +namespace Codebelt.Extensions.Carter; + +/// +/// Tests for the class. +/// +public class EndpointConventionBuilderExtensionsTest : Test +{ + public EndpointConventionBuilderExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Produces_WithTResponse_ShouldAddProducesResponseTypeMetadata() + { + var delegateEndpointBuilder = new FakeEndpointConventionBuilder(); + + delegateEndpointBuilder.Produces(StatusCodes.Status201Created, "application/json", "text/plain"); + + var endpointBuilder = new RouteEndpointBuilder(context => System.Threading.Tasks.Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); + foreach (var convention in delegateEndpointBuilder.Conventions) + { + convention(endpointBuilder); + } + + var typeMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault(); + + Assert.NotNull(typeMetadata); + Assert.Equal(StatusCodes.Status201Created, typeMetadata.StatusCode); + Assert.Equal(typeof(string), typeMetadata.Type); + Assert.Contains("application/json", typeMetadata.ContentTypes); + Assert.Contains("text/plain", typeMetadata.ContentTypes); + } + + [Fact] + public void Produces_WithTResponse_DefaultStatusCode_ShouldAddProducesResponseTypeMetadata() + { + var delegateEndpointBuilder = new FakeEndpointConventionBuilder(); + + delegateEndpointBuilder.Produces(contentTypes: "application/xml"); + + var endpointBuilder = new RouteEndpointBuilder(context => System.Threading.Tasks.Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); + foreach (var convention in delegateEndpointBuilder.Conventions) + { + convention(endpointBuilder); + } + + var typeMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault(); + + Assert.NotNull(typeMetadata); + Assert.Equal(StatusCodes.Status200OK, typeMetadata.StatusCode); + Assert.Equal(typeof(int), typeMetadata.Type); + Assert.Contains("application/xml", typeMetadata.ContentTypes); + } + + [Fact] + public void Produces_WithStatusCode_ShouldAddProducesResponseTypeMetadata() + { + var delegateEndpointBuilder = new FakeEndpointConventionBuilder(); + + delegateEndpointBuilder.Produces(StatusCodes.Status404NotFound); + + var endpointBuilder = new RouteEndpointBuilder(context => System.Threading.Tasks.Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); + foreach (var convention in delegateEndpointBuilder.Conventions) + { + convention(endpointBuilder); + } + + var typeMetadata = endpointBuilder.Metadata.OfType().FirstOrDefault(); + + Assert.NotNull(typeMetadata); + Assert.Equal(StatusCodes.Status404NotFound, typeMetadata.StatusCode); + Assert.Null(typeMetadata.Type); + // ContentTypes might be empty + Assert.Empty(typeMetadata.ContentTypes); + } + + private class FakeEndpointConventionBuilder : IEndpointConventionBuilder + { + public List> Conventions { get; } = []; + + public void Add(Action convention) + { + Conventions.Add(convention); + } + } +} diff --git a/test/Codebelt.Extensions.Carter.Tests/Response/ConfigurableResponseNegotiatorTest.cs b/test/Codebelt.Extensions.Carter.Tests/Response/ConfigurableResponseNegotiatorTest.cs index 0aa3dbd..8ca946e 100644 --- a/test/Codebelt.Extensions.Carter.Tests/Response/ConfigurableResponseNegotiatorTest.cs +++ b/test/Codebelt.Extensions.Carter.Tests/Response/ConfigurableResponseNegotiatorTest.cs @@ -1,149 +1,148 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit; -using Cuemon.Configuration; -using Cuemon.Diagnostics; -using Cuemon.Net.Http; -using Cuemon.Runtime.Serialization.Formatters; -using Microsoft.AspNetCore.Http; -using Microsoft.Net.Http.Headers; -using Xunit; - -namespace Codebelt.Extensions.Carter.Response -{ - public class ConfigurableResponseNegotiatorTest : Test - { - public ConfigurableResponseNegotiatorTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void CanHandle_ShouldReturnTrue_WhenAcceptHeaderMatches() - { - var options = new FakeOptions(); - options.SupportedMediaTypes = new[] { new System.Net.Http.Headers.MediaTypeHeaderValue("application/json") }; - var negotiator = new FakeResponseNegotiator(options); - - var result = negotiator.CanHandle(new MediaTypeHeaderValue("application/json")); - - Assert.True(result); - Assert.Equal("application/json", negotiator.ContentType); - } - - [Fact] - public void CanHandle_ShouldReturnFalse_WhenAcceptHeaderDoesNotMatch() - { - var options = new FakeOptions(); - options.SupportedMediaTypes = new[] { new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml") }; - var negotiator = new FakeResponseNegotiator(options); - - var result = negotiator.CanHandle(new MediaTypeHeaderValue("application/json")); - - Assert.False(result); - } - - [Fact] - public void GetEncoding_ShouldReturnPreferredEncoding_FromRequest() - { - var options = new FakeOptions(); - var negotiator = new FakeResponseNegotiator(options); - - var context = new DefaultHttpContext(); - context.Request.Headers.Append("Accept-Charset", "utf-16;q=1.0, utf-8;q=0.8"); - - var encoding = negotiator.GetEncoding(context.Request); - - Assert.Equal(Encoding.Unicode, encoding); - } - - [Fact] - public void GetEncoding_ShouldReturnDefaultEncoding_WhenNoMatch() - { - var options = new FakeOptions(); - var negotiator = new FakeResponseNegotiator(options); - - var context = new DefaultHttpContext(); - context.Request.Headers.Append("Accept-Charset", "unknown-encoding;q=1.0"); - - var encoding = negotiator.GetEncoding(context.Request); - - Assert.Equal("utf-8", encoding.WebName); - } - - [Fact] - public async Task Handle_ShouldWriteToResponse() - { - var options = new FakeOptions(); - options.SupportedMediaTypes = new[] { new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain") }; - var negotiator = new FakeResponseNegotiator(options); - - var context = new DefaultHttpContext(); - var ms = new MemoryStream(); - context.Response.Body = ms; - - // Simulate CanHandle to set ContentType - negotiator.CanHandle(new MediaTypeHeaderValue("text/plain")); - - await negotiator.Handle(context.Request, context.Response, "Hello World", CancellationToken.None); - - var result = Encoding.UTF8.GetString(ms.ToArray()); - - Assert.Equal("Hello World", result); - Assert.Equal("text/plain; charset=utf-8", context.Response.ContentType); - } - } - - public class FakeResponseNegotiator : ConfigurableResponseNegotiator - { - public FakeResponseNegotiator(FakeOptions options) : base(options) - { - } - - protected override Encoding GetDefaultEncoding() - { - return new UTF8Encoding(false); - } - - public override StreamFormatter GetFormatter() - { - return new FakeStreamFormatter(o => { }); - } - } - - public class FakeOptions : IExceptionDescriptorOptions, IContentNegotiation, IValidatableParameterObject - { - public FaultSensitivityDetails SensitivityDetails { get; set; } - - public IReadOnlyCollection SupportedMediaTypes { get; set; } = new List(); - - public void ValidateOptions() - { - } - } - - public class FakeStreamFormatter : StreamFormatter - { - public FakeStreamFormatter(Action setup) : base(setup) - { - } - - public override Stream Serialize(object source, Type objectType) - { - var ms = new MemoryStream(); - var writer = new StreamWriter(ms); - writer.Write(source?.ToString()); - writer.Flush(); - ms.Position = 0; - return ms; - } - - public override object Deserialize(Stream stream, Type objectType) - { - throw new NotImplementedException(); - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit; +using Cuemon.Configuration; +using Cuemon.Diagnostics; +using Cuemon.Net.Http; +using Cuemon.Runtime.Serialization.Formatters; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; +using Xunit; + +namespace Codebelt.Extensions.Carter.Response; + +public class ConfigurableResponseNegotiatorTest : Test +{ + public ConfigurableResponseNegotiatorTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void CanHandle_ShouldReturnTrue_WhenAcceptHeaderMatches() + { + var options = new FakeOptions(); + options.SupportedMediaTypes = new[] { new System.Net.Http.Headers.MediaTypeHeaderValue("application/json") }; + var negotiator = new FakeResponseNegotiator(options); + + var result = negotiator.CanHandle(new MediaTypeHeaderValue("application/json")); + + Assert.True(result); + Assert.Equal("application/json", negotiator.ContentType); + } + + [Fact] + public void CanHandle_ShouldReturnFalse_WhenAcceptHeaderDoesNotMatch() + { + var options = new FakeOptions(); + options.SupportedMediaTypes = new[] { new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml") }; + var negotiator = new FakeResponseNegotiator(options); + + var result = negotiator.CanHandle(new MediaTypeHeaderValue("application/json")); + + Assert.False(result); + } + + [Fact] + public void GetEncoding_ShouldReturnPreferredEncoding_FromRequest() + { + var options = new FakeOptions(); + var negotiator = new FakeResponseNegotiator(options); + + var context = new DefaultHttpContext(); + context.Request.Headers.Append("Accept-Charset", "utf-16;q=1.0, utf-8;q=0.8"); + + var encoding = negotiator.GetEncoding(context.Request); + + Assert.Equal(Encoding.Unicode, encoding); + } + + [Fact] + public void GetEncoding_ShouldReturnDefaultEncoding_WhenNoMatch() + { + var options = new FakeOptions(); + var negotiator = new FakeResponseNegotiator(options); + + var context = new DefaultHttpContext(); + context.Request.Headers.Append("Accept-Charset", "unknown-encoding;q=1.0"); + + var encoding = negotiator.GetEncoding(context.Request); + + Assert.Equal("utf-8", encoding.WebName); + } + + [Fact] + public async Task Handle_ShouldWriteToResponse() + { + var options = new FakeOptions(); + options.SupportedMediaTypes = new[] { new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain") }; + var negotiator = new FakeResponseNegotiator(options); + + var context = new DefaultHttpContext(); + var ms = new MemoryStream(); + context.Response.Body = ms; + + // Simulate CanHandle to set ContentType + negotiator.CanHandle(new MediaTypeHeaderValue("text/plain")); + + await negotiator.Handle(context.Request, context.Response, "Hello World", CancellationToken.None); + + var result = Encoding.UTF8.GetString(ms.ToArray()); + + Assert.Equal("Hello World", result); + Assert.Equal("text/plain; charset=utf-8", context.Response.ContentType); + } +} + +public class FakeResponseNegotiator : ConfigurableResponseNegotiator +{ + public FakeResponseNegotiator(FakeOptions options) : base(options) + { + } + + protected override Encoding GetDefaultEncoding() + { + return new UTF8Encoding(false); + } + + public override StreamFormatter GetFormatter() + { + return new FakeStreamFormatter(o => { }); + } +} + +public class FakeOptions : IExceptionDescriptorOptions, IContentNegotiation, IValidatableParameterObject +{ + public FaultSensitivityDetails SensitivityDetails { get; set; } + + public IReadOnlyCollection SupportedMediaTypes { get; set; } = new List(); + + public void ValidateOptions() + { + } +} + +public class FakeStreamFormatter : StreamFormatter +{ + public FakeStreamFormatter(Action setup) : base(setup) + { + } + + public override Stream Serialize(object source, Type objectType) + { + var ms = new MemoryStream(); + var writer = new StreamWriter(ms); + writer.Write(source?.ToString()); + writer.Flush(); + ms.Position = 0; + return ms; + } + + public override object Deserialize(Stream stream, Type objectType) + { + throw new NotImplementedException(); + } +} diff --git a/testenvironments.json b/testenvironments.json index 587b2ef..0651f28 100644 --- a/testenvironments.json +++ b/testenvironments.json @@ -1,15 +1,20 @@ -{ - "version": "1", - "environments": [ - { - "name": "WSL-Ubuntu", - "type": "wsl", - "wslDistribution": "Ubuntu-24.04" - }, - { - "name": "Docker-Ubuntu", - "type": "docker", - "dockerImage": "codebeltnet/ubuntu-testrunner:8-9-10-11" - } - ] -} +{ + "version": "1", + "environments": [ + { + "name": "WSL-Ubuntu", + "type": "wsl", + "wslDistribution": "Ubuntu-26.04" + }, + { + "name": "Docker-Ubuntu (net10.0)", + "type": "docker", + "dockerImage": "codebeltnet/ubuntu-testrunner:10" + }, + { + "name": "Docker-Ubuntu (net11.0)", + "type": "docker", + "dockerImage": "codebeltnet/ubuntu-testrunner:11" + } + ] +}