Skip to content

Commit

Permalink
Merge branch 'main' into fork/adrum/feature/defer-prop
Browse files Browse the repository at this point in the history
  • Loading branch information
kapi2289 committed Jan 6, 2025
2 parents f362bda + e5d3c25 commit 298eaa0
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 59 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ name: .NET

on:
push:
branches: [ "main" ]
branches:
- "main"
- "v1"
pull_request:
branches: [ "main" ]
branches:
- "main"
- "v1"

jobs:
build:
Expand Down
5 changes: 2 additions & 3 deletions InertiaCore/Extensions/Configure.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.IO.Abstractions;
using System.Net;
using InertiaCore.Models;
using InertiaCore.Ssr;
Expand All @@ -25,7 +24,7 @@ public static IApplicationBuilder UseInertia(this IApplicationBuilder app)
{
if (context.IsInertiaRequest()
&& context.Request.Method == "GET"
&& context.Request.Headers[Header.Version] != Inertia.GetVersion())
&& context.Request.Headers[InertiaHeader.Version] != Inertia.GetVersion())
{
await OnVersionChange(context, app);
return;
Expand Down Expand Up @@ -69,7 +68,7 @@ private static async Task OnVersionChange(HttpContext context, IApplicationBuild

if (tempData.Any()) tempData.Keep();

context.Response.Headers.Override(Header.Location, context.RequestedUri());
context.Response.Headers.Override(InertiaHeader.Location, context.RequestedUri());
context.Response.StatusCode = (int)HttpStatusCode.Conflict;

await context.Response.CompleteAsync();
Expand Down
37 changes: 18 additions & 19 deletions InertiaCore/Extensions/InertiaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,49 @@ internal static class InertiaExtensions
{
internal static Dictionary<string, object?> OnlyProps(this ActionContext context, Dictionary<string, object?> props)
{
var onlyKeys = context.HttpContext.Request.Headers[Header.PartialOnly].ToString().Split(',').Select(k => k.Trim()).ToList();
var onlyKeys = context.HttpContext.Request.Headers[InertiaHeader.PartialOnly]
.ToString().Split(',')
.Select(k => k.Trim())
.Where(k => !string.IsNullOrEmpty(k))
.ToList();

return props.Where(kv => onlyKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase))
.ToDictionary(kv => kv.Key, kv => kv.Value);
}

internal static Dictionary<string, object?> ExceptProps(this ActionContext context, Dictionary<string, object?> props)
internal static Dictionary<string, object?> ExceptProps(this ActionContext context,
Dictionary<string, object?> props)
{
var exceptKeys = context.HttpContext.Request.Headers[Header.PartialExcept].ToString().Split(',').Select(k => k.Trim()).ToList();
var exceptKeys = context.HttpContext.Request.Headers[InertiaHeader.PartialExcept]
.ToString().Split(',')
.Select(k => k.Trim())
.Where(k => !string.IsNullOrEmpty(k))
.ToList();

return props.Where(kv => exceptKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase) == false)
.ToDictionary(kv => kv.Key, kv => kv.Value);
}

internal static List<string> GetPartialData(this ActionContext context) =>
context.HttpContext.Request.Headers[Header.PartialOnly]
.FirstOrDefault()?.Split(",")
.Where(s => !string.IsNullOrEmpty(s))
.ToList() ?? new List<string>();

internal static bool IsInertiaPartialComponent(this ActionContext context, string component) =>
context.HttpContext.Request.Headers[Header.PartialComponent] == component;
context.HttpContext.Request.Headers[InertiaHeader.PartialComponent] == component;

internal static string RequestedUri(this HttpContext context) =>
Uri.UnescapeDataString(context.Request.GetEncodedPathAndQuery());

internal static string RequestedUri(this ActionContext context) => context.HttpContext.RequestedUri();

internal static bool IsInertiaRequest(this HttpContext context) =>
bool.TryParse(context.Request.Headers[Header.Inertia], out _);
bool.TryParse(context.Request.Headers[InertiaHeader.Inertia], out _);

internal static bool IsInertiaRequest(this ActionContext context) => context.HttpContext.IsInertiaRequest();

internal static string ToCamelCase(this string s) => JsonNamingPolicy.CamelCase.ConvertName(s);

internal static bool Override<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
if (dictionary.ContainsKey(key))
{
dictionary[key] = value;
return true;
}

dictionary.Add(key, value);
return false;
if (dictionary.TryAdd(key, value)) return false;
dictionary[key] = value;

return true;
}
}
1 change: 0 additions & 1 deletion InertiaCore/Inertia.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using System.Text;
using InertiaCore.Utils;
using Microsoft.AspNetCore.Html;

Expand Down
25 changes: 14 additions & 11 deletions InertiaCore/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected internal void ProcessResponse()

protected internal JsonResult GetJson()
{
_context!.HttpContext.Response.Headers.Override(Header.Inertia, "true");
_context!.HttpContext.Response.Headers.Override(InertiaHeader.Inertia, "true");
_context!.HttpContext.Response.Headers.Override("Vary", "Accept");
_context!.HttpContext.Response.StatusCode = 200;

Expand Down Expand Up @@ -118,27 +118,30 @@ public Response WithViewData(IDictionary<string, object> viewData)

private Dictionary<string, object?> ResolveProperties(Dictionary<string, object?> props)
{
bool isPartial = _context!.IsInertiaPartialComponent(_component);
var isPartial = _context!.IsInertiaPartialComponent(_component);

if (!isPartial)
{
props = props
.Where(kv => (kv.Value as IgnoreFirstLoad) == null)
.ToDictionary(kv => kv.Key, kv => kv.Value);
}

if (isPartial && _context!.HttpContext.Request.Headers.ContainsKey(Header.PartialOnly))
else
{
props = ResolveOnly(props);
}
props = props.ToDictionary(kv => kv.Key, kv => kv.Value);

if (isPartial && _context!.HttpContext.Request.Headers.ContainsKey(Header.PartialExcept))
{
props = ResolveExcept(props);
if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialOnly))
{
props = ResolveOnly(props);
}

if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialExcept))
{
props = ResolveExcept(props);
}
}

props = ResolveAlways(props);

props = PrepareProps(props);

return props;
Expand Down Expand Up @@ -169,7 +172,7 @@ public Response WithViewData(IDictionary<string, object> viewData)
{
// Parse the "RESET" header into a collection of keys to reset
var resetProps = new HashSet<string>(
_context!.HttpContext.Request.Headers[Header.Reset]
_context!.HttpContext.Request.Headers[InertiaHeader.Reset]
.ToString()
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim()),
Expand Down
21 changes: 8 additions & 13 deletions InertiaCore/Utils/AlwaysProp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@ public AlwaysProp(object? value)
{
// Check if the value is a callable delegate
return Task.Run(async () =>
{
if (_value is Func<Task<object?>> asyncCallable)
{
return await asyncCallable.Invoke();
}

if (_value is Func<object?> callable)
{
return callable.Invoke();
}

return _value;
}).GetAwaiter().GetResult();
{
return _value switch
{
Func<Task<object?>> asyncCallable => await asyncCallable.Invoke(),
Func<object?> callable => callable.Invoke(),
_ => _value
};
}).GetAwaiter().GetResult();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace InertiaCore.Utils;

public static class Header
public static class InertiaHeader
{
public const string Inertia = "X-Inertia";

Expand Down
2 changes: 1 addition & 1 deletion InertiaCore/Utils/LocationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public async Task ExecuteResultAsync(ActionContext context)
{
if (context.IsInertiaRequest())
{
context.HttpContext.Response.Headers.Override(Header.Location, _url);
context.HttpContext.Response.Headers.Override(InertiaHeader.Location, _url);
await new StatusCodeResult((int)HttpStatusCode.Conflict).ExecuteResultAsync(context);
return;
}
Expand Down
6 changes: 2 additions & 4 deletions InertiaCoreTests/UnitTestAlwaysData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ public void TestAlwaysData()
{
Test = "Test",
TestFunc = new Func<string>(() => "Func"),
TestAlways = _factory.Always(() =>
{
return "Always";
})
TestAlways = _factory.Always(() => "Always")
});

var context = PrepareContext();
Expand Down Expand Up @@ -155,6 +152,7 @@ public void TestAlwaysAsyncPartialDataOmitted()
var headers = new HeaderDictionary
{
{ "X-Inertia-Partial-Data", "testFunc" },
{ "X-Inertia-Partial-Except", "testAlways" },
{ "X-Inertia-Partial-Component", "Test/Page" }
};

Expand Down
118 changes: 117 additions & 1 deletion InertiaCoreTests/UnitTestPartialData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public partial class Tests
{
[Test]
[Description("Test if the props contain only the specified partial data.")]
public void TestPartialData()
public void TestPartialOnlyData()
{
var response = _factory.Render("Test/Page", new
{
Expand Down Expand Up @@ -44,4 +44,120 @@ public void TestPartialData()
{ "errors", new Dictionary<string, string>(0) }
}));
}

[Test]
[Description("Test if the props contain all the unspecified data")]
public void TestPartialFullData()
{
var response = _factory.Render("Test/Page", new
{
Test = "Test",
TestPartial = "Partial",
TestFunc = new Func<string>(() => "Func"),
TestLazy = _factory.Lazy(() => "Lazy")
});

var headers = new HeaderDictionary
{
{ "X-Inertia-Partial-Component", "Test/Page" }
};

var context = PrepareContext(headers);

response.SetContext(context);
response.ProcessResponse();

var page = response.GetJson().Value as Page;

Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
{
{ "test", "Test" },
{ "testPartial", "Partial" },
{ "testFunc", "Func" },
{ "testLazy", "Lazy" },
{ "errors", new Dictionary<string, string>(0) }
}));
}

[Test]
[Description("Test if the props contain none of the except data")]
public void TestPartialExceptData()
{
var response = _factory.Render("Test/Page", new
{
Test = "Test",
TestPartial = "Partial",
TestFunc = new Func<string>(() =>
{
Assert.Fail();
return "Func";
}),
TestLazy = _factory.Lazy(() =>
{
Assert.Fail();
return "Lazy";
})
});

var headers = new HeaderDictionary
{
{ "X-Inertia-Partial-Except", "TestFunc,TestLazy" },
{ "X-Inertia-Partial-Component", "Test/Page" }
};

var context = PrepareContext(headers);

response.SetContext(context);
response.ProcessResponse();

var page = response.GetJson().Value as Page;

Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
{
{ "test", "Test" },
{ "testPartial", "Partial" },
{ "errors", new Dictionary<string, string>(0) }
}));
}

[Test]
[Description("Test if the props contain the correct data when using only and except data")]
public void TestPartialOnlyAndExceptData()
{
var response = _factory.Render("Test/Page", new
{
Test = "Test",
TestPartial = "Partial",
TestFunc = new Func<string>(() =>
{
Assert.Fail();
return "Func";
}),
TestLazy = _factory.Lazy(() =>
{
Assert.Fail();
return "Lazy";
})
});

var headers = new HeaderDictionary
{
{ "X-Inertia-Partial-Data", "Test,TestFunc,TestLazy" },
{ "X-Inertia-Partial-Except", "TestFunc,TestLazy" },
{ "X-Inertia-Partial-Component", "Test/Page" }
};

var context = PrepareContext(headers);

response.SetContext(context);
response.ProcessResponse();

var page = response.GetJson().Value as Page;

Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
{
{ "test", "Test" },
{ "errors", new Dictionary<string, string>(0) }
}));
}
}
6 changes: 3 additions & 3 deletions InertiaCoreTests/UnitTestResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ public void TestJsonMergeDeferredResult()

var headers = new HeaderDictionary
{
{ Header.Inertia, "true" },
{ Header.PartialComponent, "Test/Page" },
{ Header.PartialOnly, "testMerged" },
{ InertiaHeader.Inertia, "true" },
{ InertiaHeader.PartialComponent, "Test/Page" },
{ InertiaHeader.PartialOnly, "testMerged" },
};

var context = PrepareContext(headers);
Expand Down

0 comments on commit 298eaa0

Please sign in to comment.