BlogPost is shared between the server (Yavsc.Server/Models/Blog/
BlogPost.cs is the EF entity) and any client that talks to the
blogs API. Keeping the client-side DTO in PostIt.Models made
sense when there was only one consumer; now that the
Yavsc.Api.Client project is about to host BlogApiClient alongside
CircleApiClient and BlogAclApiClient, the DTO has to live in a
layer both the client project and PostIt can reference without
inverting the dependency.
Yavsc.Abstract is the existing home for cross-tier interfaces
and DTOs (IBlogPost, IBlogPostPayLoad, IApplicationUser).
Yavsc.Blogspot is the sub-namespace already used by the
matching interface, so the new concrete class follows.
Why not move Circle and CircleAuthorizationToBlogPost at the
same time? Both depend on the concrete ApplicationUser class
(via the Owner and Target/Allowed navigation properties) which
lives in Yavsc.Server. Moving them would mean either dragging
ApplicationUser into the abstract layer (huge blast radius —
auth, billing, chat, etc.) or weakening the navigation
properties (breaks EF Core shaping). They're staying where
they are; the new Yavsc.Api.Client will get DTO counterparts
instead.
Updated call sites:
- 4 .cs files: replace 'using PostIt.Models;' with
'using Yavsc.Blogspot;' where the file was actually using
BlogPost. Files that only used SignaturePadData keep their
'using PostIt.Models;' — that type stays put.
- 1 .axaml file: xmlns:models="using:PostIt.Models" ->
xmlns:models="using:Yavsc.Blogspot" (one DataTemplate for
the post list in MainPage).
Build + tests green (51/51).
The 'Save' button in PostIt has been returning 400 from
/api/v1/blog ever since the editor's title and article fields
were re-bound to SelectedPost.Title / SelectedPost.Article.
The user types into the editor, taps Save, the controller
rejects with 'The Title field is required', and the PostIt
status bar shows only the generic 'Response status code does
not indicate success: 400' — no field name, no reason.
Three pieces here make the regression diagnosable and pin a
test for the fix:
1. YavscApiClient: replace EnsureSuccessStatusCode() at both
call sites with a small helper that reads the response
body and embeds it in the thrown HttpRequestException. The
VM's existing catch (Exception) in ExecuteAsync forwards
ex.Message to the status bar, so the next 'click Save'
tells the user exactly which field the server rejected.
2. Yavsc.Blogs.Tests: two integration tests on the real
controller (no HTTP mock) — one pins that a well-formed
PostIt-shaped payload (Title + Article + AuthorId + dates,
Id=0) is accepted with 201, the other pins that a payload
with Title=string.Empty is rejected with 400. Together they
pin the contract the VM has to honour.
3. PostIt.Tests: a red [AvaloniaFact] UI test that mounts
MainPage inside a headless Window, types a title into the
TextBox without first selecting a post in the list, taps
Save, and asserts the body of the first POST contains
the typed title. Today this test fails with Title='',
reproducing the production 400. The matching fix (a
Title/Article buffer on MainPageViewModel that the XAML
binds to, and that Save uses to build the outgoing
BlogPost) is the next commit; the test is the safety net.