diff --git a/src/PostIt/PostIt/Views/MainPage.axaml b/src/PostIt/PostIt/Views/MainPage.axaml index e30d1a84..7eac39ff 100644 --- a/src/PostIt/PostIt/Views/MainPage.axaml +++ b/src/PostIt/PostIt/Views/MainPage.axaml @@ -67,7 +67,7 @@ + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="40"> diff --git a/src/Yavsc.Blogs.Tests/CircleMembersApiTests.cs b/src/Yavsc.Blogs.Tests/CircleMembersApiTests.cs index 4e9bfb00..5e8040ef 100644 --- a/src/Yavsc.Blogs.Tests/CircleMembersApiTests.cs +++ b/src/Yavsc.Blogs.Tests/CircleMembersApiTests.cs @@ -88,7 +88,7 @@ public sealed class CircleMembersApiTests : IClassFixture } private string MembersUrl(long circleId) - => $"{_fixture.Addresses.First(a => a.StartsWith("https://"))}/api/circle/{circleId}/members"; + => $"{_fixture.Addresses.First(a => a.StartsWith("https://"))}/{Constants.APIPrefix}/circle/{circleId}/members"; private HttpClient NewClient(string subject) { diff --git a/src/Yavsc.Blogs/Controllers/BlogApiController.cs b/src/Yavsc.Blogs/Controllers/BlogApiController.cs index 3901d9cb..76aa777e 100644 --- a/src/Yavsc.Blogs/Controllers/BlogApiController.cs +++ b/src/Yavsc.Blogs/Controllers/BlogApiController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Yavsc.Blogspot; -using Yavsc.Models.Blog; using Yavsc.Server.Exceptions; using Yavsc.Server.Helpers; using static Yavsc.Blogs.Constants; diff --git a/src/Yavsc.Blogs/Controllers/CircleApiController.cs b/src/Yavsc.Blogs/Controllers/CircleApiController.cs index c35da7c5..73bbfff9 100644 --- a/src/Yavsc.Blogs/Controllers/CircleApiController.cs +++ b/src/Yavsc.Blogs/Controllers/CircleApiController.cs @@ -4,11 +4,12 @@ using Microsoft.EntityFrameworkCore; using Yavsc.Models; using Yavsc.Models.Relationship; using Yavsc.Server.Helpers; +using static Yavsc.Blogs.Constants; namespace Yavsc.Blogs.Controllers { [Produces("application/json")] - [Route("api/circle")] + [Route(APIPrefix +"/circle")] public class CircleApiController : Controller { private readonly ApplicationDbContext _context; @@ -56,12 +57,25 @@ namespace Yavsc.Blogs.Controllers /// /// Replaces a circle. The caller must own it; the server - /// reasserts ownership regardless of any OwnerId the client - /// tries to put in the body. + /// reasserts ownership regardless of any OwnerId + /// the client tries to put in the body. + /// + /// The body shape is a — a + /// flat, navigation-free projection — not the EF entity. + /// The EF entity carries [JsonIgnore]-decorated + /// navigation properties (Owner, Members) + /// that bind to server-only types (ApplicationUser, + /// CircleMember); keeping the wire shape as a + /// DTO avoids any future regression where the entity + /// grows a navigable property that System.Text.Json + /// refuses to materialise. The client-side mirror lives + /// in Yavsc.Api.Client.Dtos.CircleDto. /// // PUT: api/circle/5 [HttpPut("{id}")] - public async Task PutCircle([FromRoute] long id, [FromBody] Circle circle) + public async Task PutCircle( + [FromRoute] long id, + [FromBody] CircleDto circle) { if (!ModelState.IsValid) { @@ -81,9 +95,14 @@ namespace Yavsc.Blogs.Controllers return new ChallengeResult(); } - // Force OwnerId to the caller; the body value is ignored. - circle.OwnerId = uid; - _context.Entry(circle).State = EntityState.Modified; + // Map the wire shape onto the entity. OwnerId is + // forced to the caller regardless of what the body + // says; Name and Public come from the body. + existing.Name = circle.Name; + existing.Public = circle.Public; + existing.OwnerId = uid; + + _context.Entry(existing).State = EntityState.Modified; try { @@ -110,7 +129,7 @@ namespace Yavsc.Blogs.Controllers /// // POST: api/circle [HttpPost] - public async Task PostCircle([FromBody] Circle circle) + public async Task PostCircle([FromBody] CircleDto circle) { if (!ModelState.IsValid) { @@ -119,8 +138,14 @@ namespace Yavsc.Blogs.Controllers var uid = User.GetUserId(); circle.OwnerId = uid; + Circle newCircle = new Circle + { + OwnerId = User.GetUserId(), + Name = circle.Name, + Public = circle.Public + }; - _context.Circle.Add(circle); + _context.Circle.Add(newCircle); try { await _context.SaveChangesAsync(User.GetUserId()); @@ -321,6 +346,26 @@ namespace Yavsc.Blogs.Controllers } } + /// + /// Wire shape for PUT /api/circle/{id}. Flat by + /// design — navigation properties (Owner, + /// Members) live on the EF entity only and never + /// cross the wire. + /// + /// Field names match the JSON the server emits + /// (camelCase via ASP.NET Core's Web defaults), so no + /// [JsonPropertyName] attributes are required. + /// Mirrors the client-side Yavsc.Api.Client.Dtos.CircleDto + /// — keep them in sync. + /// + public sealed class CircleDto + { + public long Id { get; set; } + public string Name { get; set; } = string.Empty; + public string OwnerId { get; set; } = string.Empty; + public bool Public { get; set; } + } + /// /// Wire shape for GET /api/circle/{id}/members. /// Mirrors but stops