Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/guide/clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ setting.

With `--serializer message-pack-named` or `message-pack-keyed` and `--client refit`, the client
project also holds two Liquid templates in `templates/`, `MessagePackContentSerializer.cs`, and a
reference to the `MessagePack` package, 3.1.8. `.refitter` then names the folder in
reference to the `MessagePack` package, 3.1.9. `.refitter` then names the folder in
`customTemplateDirectory`. It also sets `generateDefaultAdditionalProperties` to `false`.

The templates put these attributes on the generated models:
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/message-pack.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Run this command in the solution directory:
dotnet add src/Todos package Hardened.Requests.Serializers.MessagePack --version 0.0.0-HARDENED-VERSION
```

The package depends on `MessagePack` 3.1.8, which brings MessagePack's source generator into the project.
The package depends on `MessagePack` 3.1.9, which brings MessagePack's source generator into the project.

The template's solution uses central package management. There, the command adds a `PackageReference` to `src/Todos/Todos.csproj` and a `PackageVersion` to `Directory.Packages.props`. The `PackageVersion` holds the version as a literal. The template's other Hardened packages use `$(HardenedVersion)`.

Expand Down
41 changes: 41 additions & 0 deletions scripts/probes/JsonDomController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Hardened.Requests.Abstract.Attributes;
using Hardened.Web.Runtime.Attributes;

namespace Sample;

/// <summary>
/// Copied into a code-first Refit scaffold by scripts/verify-templates.sh, so the client Refitter
/// generates from the document has to compile over the System.Text.Json DOM types. Their schemas
/// once carried a property named this[], which Refitter refused.
/// </summary>
public record JsonDomEnvelope(
JsonElement Element,
JsonNode Node,
JsonObject Object,
JsonArray Array,
JsonValue Value,
JsonObject? MaybeObject
);

public class JsonDomController
{
[Post("/dom/envelope")]
public JsonDomEnvelope Envelope(JsonDomEnvelope body) => body;

[Post("/dom/element")]
public JsonElement Element([FromBody] JsonElement body) => body;

[Post("/dom/node")]
public JsonNode Node([FromBody] JsonNode body) => body;

[Post("/dom/object")]
public JsonObject Object([FromBody] JsonObject body) => body;

[Post("/dom/array")]
public JsonArray Array([FromBody] JsonArray body) => body;

[Post("/dom/value")]
public JsonValue Value([FromBody] JsonValue body) => body;
}
23 changes: 23 additions & 0 deletions scripts/verify-templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,29 @@ for COMBO in "${COMBOS[@]}"; do

run_tests "$OUT" GetTodo_ReadsTheMockedStore

# The System.Text.Json DOM types, on the rows where Refitter reads a document the server's
# code wrote. Their schemas once carried a property named this[], which Refitter refused, and
# the template's own handlers reach none of them. After the tests, because DocumentStatusTests
# fails on an operation it sends no request to, and removed again so the probes below serve
# the application as scaffolded. Not on the MessagePack rows, whose Liquid templates key every
# generated member and have no formatter for a free-form one.
if [ "$CONTRACT" = code ] && [ "$CLIENT" = refit ] && \
{ [ "$SERIALIZER" = default ] || [ "$SERIALIZER" = json ]; }; then
cp "$REPO/scripts/probes/JsonDomController.cs" "$OUT/src/Sample/JsonDomController.cs"

if ! ( cd "$OUT" && dotnet build src/Sample.Client -v q --nologo ) > "$OUT/dom-probe.log" 2>&1; then
echo " FAILED: the Refit client does not compile over the System.Text.Json DOM types - see $OUT/dom-probe.log"
FAILED=1
elif ! grep -rq '/dom/element"' "$OUT/src/Sample.Client/obj" --include='*.cs'; then
echo " FAILED: the System.Text.Json DOM probe did not reach the Refit client"
FAILED=1
else
echo " the Refit client compiles over the System.Text.Json DOM types"
fi

rm "$OUT/src/Sample/JsonDomController.cs"
fi

# The client rows: the document the library's build wrote is what the client generated from,
# and it is the file a consumer commits - so it has to exist, and it has to be the served
# document. The opt-out row: no property, no file, no client project.
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
<PackageVersion Include="Microsoft.Kiota.Http.HttpClientLibrary" Version="2.0.0" />
<PackageVersion Include="Microsoft.OpenApi" Version="3.10.0" />
<PackageVersion Include="Microsoft.OpenApi.YamlReader" Version="3.10.0" />
<PackageVersion Include="MessagePack" Version="3.1.8" />
<PackageVersion Include="MessagePack" Version="3.1.9" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="RazorBlade" Version="1.0.0" />
<PackageVersion Include="Refit" Version="16.3.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ private static string SchemaFor(
return BinaryPayload;
}

// The System.Text.Json DOM holds whatever JSON arrived, so it is free-form on the wire.
// Reflected instead, JsonElement published its indexer as a property named this[] and
// JsonObject a required count, and Refitter refused the repeated this[] key.
var dom = JsonDom(type);

if (dom != null)
{
return dom;
}

if (type is IArrayTypeSymbol array)
{
// byte[] is the payload itself, not a sequence of numbers. Every element type below
Expand Down Expand Up @@ -274,6 +284,36 @@ private static bool IsProblemType(INamedTypeSymbol owner, IPropertySymbol proper
&& property.Type.SpecialType == SpecialType.System_String
&& Requests.ProblemBodies.Implement(owner);

/// <summary>
/// The schema for a <c>System.Text.Json</c> DOM type, or null for anything else.
/// </summary>
/// <remarks>
/// Matched on the namespace as well as the name, so an application's own type named
/// <c>JsonNode</c> is still reflected.
/// </remarks>
private static string? JsonDom(ITypeSymbol type)
{
var space = type.ContainingNamespace?.ToDisplayString();

if (space == "System.Text.Json")
{
return type.Name == "JsonElement" ? "{}" : null;
}

if (space != "System.Text.Json.Nodes")
{
return null;
}

return type.Name switch
{
"JsonObject" => "{\"type\":\"object\"}",
"JsonArray" => "{\"type\":\"array\",\"items\":{}}",
"JsonNode" or "JsonValue" => "{}",
_ => null,
};
}

/// <summary>
/// Whether a type is a <c>System.IO.Stream</c>, base types walked.
/// </summary>
Expand Down Expand Up @@ -515,6 +555,7 @@ private static string ObjectRef(
if (
property.DeclaredAccessibility != Accessibility.Public
|| property.IsStatic
|| property.IsIndexer
|| property.GetMethod == null
)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,13 @@
Link="Routing/RouteConstraintFactsTests.cs"/>
<Compile Include="../Hardened.SourceGenerator.Tests/Web/RouteTemplateTests.cs"
Link="Routing/RouteTemplateTests.cs"/>

<!--
The document format module the export task writes a file through, so a test can take
the document this generator emits to YAML and to each HardenedOpenApiOutputVersion.
The generator does not compile it in, so this is the only copy in scope.
-->
<Compile Include="../Hardened.Generation.Shared/Document/*.cs"
Link="Document/%(Filename)%(Extension)"/>
</ItemGroup>
</Project>
Loading
Loading