Skip to content

Commit d842ac8

Browse files
committed
[2.x] Keep only partial data in mergeProps
inertiajs/inertia-laravel#745
1 parent 28afc55 commit d842ac8

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

InertiaCore/Response.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,32 @@ protected internal async Task ProcessResponse()
159159
StringComparer.OrdinalIgnoreCase
160160
);
161161

162+
// Parse the "PARTIAL_ONLY" header into a collection of keys to include
163+
var onlyProps = _context!.HttpContext.Request.Headers[InertiaHeader.PartialOnly]
164+
.ToString()
165+
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
166+
.Select(s => s.Trim())
167+
.Where(s => !string.IsNullOrEmpty(s))
168+
.ToHashSet(StringComparer.OrdinalIgnoreCase);
169+
170+
// Parse the "PARTIAL_EXCEPT" header into a collection of keys to exclude
171+
var exceptProps = new HashSet<string>(
172+
_context!.HttpContext.Request.Headers[InertiaHeader.PartialExcept]
173+
.ToString()
174+
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
175+
.Select(s => s.Trim()),
176+
StringComparer.OrdinalIgnoreCase
177+
);
178+
162179
var resolvedProps = props
163180
.Select(kv => kv.Key.ToCamelCase()) // Convert property name to camelCase
164181
.ToList();
165182

166183
// Filter the props that are Mergeable and should be merged
167184
var mergeProps = _props.Where(o => o.Value is Mergeable mergeable && mergeable.ShouldMerge()) // Check if value is Mergeable and should merge
168185
.Where(kv => !resetProps.Contains(kv.Key)) // Exclude reset keys
186+
.Where(kv => onlyProps.Count == 0 || onlyProps.Contains(kv.Key)) // Include only specified keys if any
187+
.Where(kv => !exceptProps.Contains(kv.Key)) // Exclude specified keys
169188
.Select(kv => kv.Key.ToCamelCase()) // Convert property name to camelCase
170189
.Where(resolvedProps.Contains) // Filter only the props that are in the resolved props
171190
.ToList();

InertiaCoreTests/UnitTestMergeData.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,116 @@ public async Task TestNoMergeProps()
206206
Assert.That(page?.MergeProps, Is.EqualTo(null));
207207
}
208208

209+
[Test]
210+
[Description("Test if merge props are excluded when using PARTIAL_EXCEPT header.")]
211+
public async Task TestMergePropsWithPartialExcept()
212+
{
213+
var response = _factory.Render("Test/Page", new
214+
{
215+
Test = "Test",
216+
TestMerge1 = _factory.Merge(() => "Merge1"),
217+
TestMerge2 = _factory.Merge(() => "Merge2"),
218+
TestNormal = "Normal"
219+
});
220+
221+
var headers = new HeaderDictionary
222+
{
223+
{ "X-Inertia-Partial-Except", "testMerge1" },
224+
{ "X-Inertia-Partial-Component", "Test/Page" }
225+
};
226+
227+
var context = PrepareContext(headers);
228+
229+
response.SetContext(context);
230+
await response.ProcessResponse();
231+
232+
var page = response.GetJson().Value as Page;
233+
234+
Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
235+
{
236+
{ "test", "Test" },
237+
{ "testMerge2", "Merge2" },
238+
{ "testNormal", "Normal" },
239+
{ "errors", new Dictionary<string, string>(0) }
240+
}));
241+
242+
// testMerge1 should be excluded from merge props due to PARTIAL_EXCEPT header
243+
Assert.That(page?.MergeProps, Is.EqualTo(new List<string> { "testMerge2" }));
244+
}
245+
246+
[Test]
247+
[Description("Test if only specified merge props are included when using PARTIAL_ONLY header.")]
248+
public async Task TestMergePropsWithPartialOnly()
249+
{
250+
var response = _factory.Render("Test/Page", new
251+
{
252+
Test = "Test",
253+
TestMerge1 = _factory.Merge(() => "Merge1"),
254+
TestMerge2 = _factory.Merge(() => "Merge2"),
255+
TestNormal = "Normal"
256+
});
257+
258+
var headers = new HeaderDictionary
259+
{
260+
{ "X-Inertia-Partial-Data", "testMerge1,testNormal" },
261+
{ "X-Inertia-Partial-Component", "Test/Page" }
262+
};
263+
264+
var context = PrepareContext(headers);
265+
266+
response.SetContext(context);
267+
await response.ProcessResponse();
268+
269+
var page = response.GetJson().Value as Page;
270+
271+
Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
272+
{
273+
{ "testMerge1", "Merge1" },
274+
{ "testNormal", "Normal" },
275+
{ "errors", new Dictionary<string, string>(0) }
276+
}));
277+
278+
// Only testMerge1 should be in merge props since testMerge2 was not included in PARTIAL_ONLY
279+
Assert.That(page?.MergeProps, Is.EqualTo(new List<string> { "testMerge1" }));
280+
}
281+
282+
[Test]
283+
[Description("Test if merge props respect both PARTIAL_ONLY and PARTIAL_EXCEPT headers.")]
284+
public async Task TestMergePropsWithPartialOnlyAndExcept()
285+
{
286+
var response = _factory.Render("Test/Page", new
287+
{
288+
Test = "Test",
289+
TestMerge1 = _factory.Merge(() => "Merge1"),
290+
TestMerge2 = _factory.Merge(() => "Merge2"),
291+
TestMerge3 = _factory.Merge(() => "Merge3"),
292+
TestNormal = "Normal"
293+
});
294+
295+
var headers = new HeaderDictionary
296+
{
297+
{ "X-Inertia-Partial-Data", "testMerge1,testMerge2,testMerge3,testNormal" },
298+
{ "X-Inertia-Partial-Except", "testMerge2" },
299+
{ "X-Inertia-Partial-Component", "Test/Page" }
300+
};
301+
302+
var context = PrepareContext(headers);
303+
304+
response.SetContext(context);
305+
await response.ProcessResponse();
306+
307+
var page = response.GetJson().Value as Page;
308+
309+
Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
310+
{
311+
{ "testMerge1", "Merge1" },
312+
{ "testMerge3", "Merge3" },
313+
{ "testNormal", "Normal" },
314+
{ "errors", new Dictionary<string, string>(0) }
315+
}));
316+
317+
// testMerge1 and testMerge3 should be in merge props (testMerge2 excluded by PARTIAL_EXCEPT)
318+
Assert.That(page?.MergeProps, Is.EqualTo(new List<string> { "testMerge1", "testMerge3" }));
319+
}
320+
209321
}

0 commit comments

Comments
 (0)