Skip to content

Fix/raps edit permissions - #338

Open
bniedzie wants to merge 3 commits into
mainfrom
fix/raps-edit-permissions
Open

Fix/raps edit permissions#338
bniedzie wants to merge 3 commits into
mainfrom
fix/raps-edit-permissions

Conversation

@bniedzie

@bniedzie bniedzie commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

This PR addresses 2 small RAPS bugs, one in each commit.

400 on editing users' permissions and roles

  • 3 forms produced 400 errors when trying to PUT without specifying both a start date and an end date: adding a member to a role, adding a member to a permission, and adding permission to a member.
  • Error was caused by QuasarTable's submit sending empty strings for blank dates, when the API expected DateTime?
  • This commit adds a front end guard that was already present when adding a role to a member, sending null instead of an empty string.
  • This commit also adds an opt-in backend JsonConverter to cover any future paths to these endpoints without changing the general repo behavior.

Prevent role to member table from hiding view roles after PUT

  • When sending a PUT through QuasarTable in the table listing a member's roles, view roles would be hidden.
  • This was caused by the need to change the table's URL path for the PUT. While the code replaced the old path after, this resulted in a race with submit to update the table that was always lost.
  • This commit makes use of the previously-unused query field in qtable.js to allow a stable URL path with varying parameters. view roles now persist through a PUT.

@codecov-commenter

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@codecov-commenter

codecov-commenter commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 44.90%. Comparing base (4fc3947) to head (b84908b).
⚠️ Report is 9 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...eb/Classes/Utilities/EmptyStringAsNullConverter.cs 91.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #338      +/-   ##
==========================================
+ Coverage   44.70%   44.90%   +0.19%     
==========================================
  Files        1063      941     -122     
  Lines       52127    49119    -3008     
  Branches     6133     6579     +446     
==========================================
- Hits        23304    22055    -1249     
+ Misses      27848    26125    -1723     
+ Partials      975      939      -36     
Flag Coverage Δ
backend 42.14% <91.66%> (+0.03%) ⬆️
frontend 63.35% <ø> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../Areas/RAPS/Models/MemberPermissionCreateUpdate.cs 100.00% <ø> (ø)
web/Areas/RAPS/Models/RoleMemberCreateUpdate.cs 100.00% <ø> (ø)
web/Areas/RAPS/Views/Members/Permissions.cshtml 0.00% <ø> (ø)
web/Areas/RAPS/Views/Members/Roles.cshtml 0.00% <ø> (ø)
web/Areas/RAPS/Views/Permissions/Members.cshtml 0.00% <ø> (ø)
web/Areas/RAPS/Views/Roles/Members.cshtml 0.00% <ø> (ø)
...eb/Classes/Utilities/EmptyStringAsNullConverter.cs 91.66% <91.66%> (ø)

... and 607 files with indirect coverage changes

@bniedzie
bniedzie requested review from bsedwards and rlorenzo and removed request for rlorenzo September 3, 2026 22:38
@rlorenzo
rlorenzo requested a balanced review from Copilot September 3, 2026 23:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It modifies the shared qtable.js table utility consumed by many RAPS views and adds a framework-level JSON converter whose UI behavior across those views cannot be runtime-verified here, warranting human final review.

Pull request overview

This PR fixes two RAPS permission/role-editing bugs. First, three edit forms (add member to role, add member to permission, add permission to member) returned HTTP 400 when submitted without both dates, because QuasarTable.submit sent empty strings for blank dates while the API expected DateTime?/DateOnly?. Second, the member-roles table hid view-assigned roles after a PUT due to a race between restoring a swapped urlBase and the reload triggered by submit. The fix routes read-only query params through the previously-unused query field on quasarTable, keeping urlBase clean so getUpdateURL() builds correct PUT/DELETE URLs, and adds an opt-in JsonConverter plus frontend || null guards for date fields.

Changes:

  • Front end: send null instead of "" for blank dates in the three affected createBody functions, and move includeViewMembers into the stable query field instead of mutating urlBase.
  • Backend: add EmptyStringAsNullConverter<T> and apply it to the date properties of RoleMemberCreateUpdate and MemberPermissionCreateUpdate to defensively bind empty strings as null.
  • Tests: add EmptyDateBindingTests covering empty/real/null/omitted date bodies and a serialization round-trip.
File summaries
File Description
web/wwwroot/js/qtable.js Merges the query object into GET params in load(), decoupling read-only params from urlBase.
web/Classes/Utilities/EmptyStringAsNullConverter.cs New generic converter that binds empty/whitespace strings to null for nullable value types.
web/Areas/RAPS/Models/RoleMemberCreateUpdate.cs Applies the converter to StartDate/EndDate (DateOnly?).
web/Areas/RAPS/Models/MemberPermissionCreateUpdate.cs Applies the converter to StartDate/EndDate (DateTime?).
web/Areas/RAPS/Views/Roles/Members.cshtml Adds `
web/Areas/RAPS/Views/Members/Roles.cshtml Seeds query: { includeViewMembers: true }, removes the urlBase swap, keeps urlBase clean.
web/Areas/RAPS/Views/Permissions/Members.cshtml Adds `
web/Areas/RAPS/Views/Members/Permissions.cshtml Adds `
test/RAPS/EmptyDateBindingTests.cs New tests validating empty-string-as-null binding and serialization round-trip.
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

{
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low priority, non-blocking.

Both null branches in this class are unreachable. For a converter over Nullable<T>, STJ handles null itself unless HandleNull is overridden to true, so Read is never handed a Null token and Write is never handed a null value.

Checked on .NET 10 with hit counters on both branches:

  • deserializing {"startDate":null,"endDate":""} calls Read once, for the empty string
  • serializing a model with StartDate set and EndDate null calls Write once, for StartDate

Lines 16-19 and the else on 34-37 can go.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed both unreachable branches

Comment thread test/RAPS/EmptyDateBindingTests.cs Outdated
var model = DeserializeMemberPermission(
@"{""memberId"":""12345678"",""permissionId"":5,""access"":1,""startDate"":""2026-01-15T00:00:00"",""endDate"":""2026-06-30T00:00:00""}");

Assert.Equal(new DateTime(2026, 1, 15, 0, 0, 0, DateTimeKind.Local), model.StartDate);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low priority.

DateTimeKind.Local isn't asserting what it looks like it's asserting. STJ parses "2026-01-15T00:00:00" (no offset, no Z) as Unspecified, and Assert.Equal on DateTime compares ticks and ignores Kind. The same line with DateTimeKind.Utc would also pass.

Drop DateTimeKind.Local from the DateTime constructor, or assert model.StartDate.Value.Kind separately if you want it pinned.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since SonarAnalyzer S6562 expects an explicit Kind on this type of constructor, switched from DateTimeKind.Local to DateTimeKind.Unspecified.

bsedwards
bsedwards previously approved these changes Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants