Skip to content

Feature/userinfo - #273

Open
JasonRobertFrancis wants to merge 60 commits into
mainfrom
feature/userinfo
Open

Feature/userinfo#273
JasonRobertFrancis wants to merge 60 commits into
mainfrom
feature/userinfo

Conversation

@JasonRobertFrancis

Copy link
Copy Markdown
Contributor

No description provided.

@codecov-commenter

codecov-commenter commented Jul 28, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 50.15kB (2.19%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
viper-frontend-esm 2.34MB 50.15kB (2.19%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: viper-frontend-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/GenericError-*.css 164 bytes 208.06kB 0.08%
assets/GenericError-*.js 13.04kB 58.03kB 28.99% ⚠️
assets/schedule-*.js 8 bytes 55.03kB 0.01%
assets/SortableList-*.js 8 bytes 45.54kB 0.02%
assets/PhotoGallery-*.js 8 bytes 36.16kB 0.02%
assets/InstructorList-*.js -32 bytes 26.04kB -0.12%
assets/effort-*.js -5 bytes 23.76kB -0.02%
assets/Files-*.js -1.77kB 20.8kB -7.86%
assets/ContentBlockEdit-*.js -32 bytes 20.74kB -0.15%
assets/MultiYearReport-*.js 8 bytes 18.81kB 0.04%
assets/EmergencyContactForm-*.js -32 bytes 17.2kB -0.19%
assets/CmsHome-*.js 8 bytes 11.43kB 0.07%
assets/ViperFetch-*.js 53 bytes 11.22kB 0.47%
assets/SVMPhonesMaintain-*.js (New) 10.54kB 10.54kB 100.0% 🚀
assets/SVMFrequentNumberTable-*.js (New) 10.15kB 10.15kB 100.0% 🚀
assets/ReportFilterForm-*.js 8 bytes 8.87kB 0.09%
assets/RecordActionCell-*.js (New) 5.95kB 5.95kB 100.0% 🚀
assets/PhoneListMaintain-*.js (New) 5.61kB 5.61kB 100.0% 🚀
assets/ClinicalEffort-*.js 8 bytes 5.33kB 0.15%
assets/PhoneListUnitTable-*.js (New) 5.01kB 5.01kB 100.0% 🚀
assets/SchoolSummary-*.js 14 bytes 3.98kB 0.35%
assets/ManageCourseCompetencies-*.js 3 bytes 3.71kB 0.08%
assets/RecordFormDialog-*.js (New) 3.69kB 3.69kB 100.0% 🚀
assets/students-*.js -5 bytes 3.5kB -0.14%
assets/dist-*.js (Deleted) -11.57kB 0 bytes -100.0% 🗑️
assets/ExportToolbar-*.js 8 bytes 2.95kB 0.27%
assets/SVMPhones-*.js (New) 2.7kB 2.7kB 100.0% 🚀
assets/PhoneList-*.js (New) 2.12kB 2.12kB 100.0% 🚀
assets/personnel-*.js (New) 2.05kB 2.05kB 100.0% 🚀
assets/PersonSelector-*.js (New) 1.79kB 1.79kB 100.0% 🚀
assets/CmsContent-*.js (New) 462 bytes 462 bytes 100.0% 🚀
assets/Home-*.js -343 bytes 221 bytes -60.82%
assets/Home-*.js (New) 221 bytes 221 bytes 100.0% 🚀
assets/RecordActionCell-*.css (New) 215 bytes 215 bytes 100.0% 🚀
assets/SVMPhones-*.css (New) 110 bytes 110 bytes 100.0% 🚀

Files in assets/SchoolSummary-*.js:

  • ./src/Effort/pages/SchoolSummary.vue → Total Size: 230 bytes

@codecov-commenter

codecov-commenter commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.40%. Comparing base (c6f64b5) to head (fe3fdd4).
⚠️ Report is 18 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #273       +/-   ##
===========================================
+ Coverage   42.38%   63.40%   +21.01%     
===========================================
  Files         994      159      -835     
  Lines       49877     6222    -43655     
  Branches     5887     1330     -4557     
===========================================
- Hits        21142     3945    -17197     
+ Misses      27798     2049    -25749     
+ Partials      937      228      -709     
Flag Coverage Δ
backend ?
frontend 63.40% <100.00%> (+4.44%) ⬆️

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

Files with missing lines Coverage Δ
VueApp/src/CMS/pages/ImportFiles.vue 68.42% <100.00%> (ø)

... and 915 files with indirect coverage changes

Comment thread test/Services/UserInfoServiceUnitTests.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Classes/Utilities/IamApi.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Views/UserInfo.cshtml Fixed

@github-advanced-security github-advanced-security 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.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Classes/Utilities/IamApi.cs Fixed
@rlorenzo

This comment was marked as resolved.

Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
@rlorenzo

This comment was marked as resolved.

Comment thread web/Areas/RAPS/Services/UinformService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/RAPS/Services/UinformService.cs Fixed
@rlorenzo
rlorenzo requested a balanced review from Copilot August 20, 2026 16:01
@JasonRobertFrancis

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review skipped: 221 files exceed the limit of 100.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

Pull request overview

Copilot reviewed 219 out of 221 changed files in this pull request and generated no new comments.

Suppressed comments (1)

web/Areas/Directory/Models/IndividualSearchResultWithIDs.cs:49

  • This branch removes the null-guard on PostalAddress, but LdapUserContact.PostalAddress is declared = null! and is only assigned when the LDAP entry actually contains a postalAddress attribute, so it can be null at runtime. For a contact without a postal address this will throw a NullReferenceException. Note the base class IndividualSearchResult handles the same field null-safely (ldapUserContact.PostalAddress?.Replace(...) ?? ""), so the two paths are now inconsistent. Please restore the null-safe access here.
                PostalAddress = ldapUserContact.PostalAddress.Replace("$", '\n'.ToString());

@rlorenzo rlorenzo left a comment

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.

Fresh pass over the branch at c18d9c8. Three authorization and student-data issues, two logic bugs in the Instinct lookup.

Comment thread web/Areas/Directory/Controllers/UserInfoController.cs Outdated
Comment thread web/Areas/Directory/Controllers/UserInfoController.cs Outdated
Comment thread web/Areas/Directory/Views/UserInfo.cshtml
Comment thread web/Areas/Directory/Services/UserInfoService.cs Outdated
Comment thread web/Areas/Directory/Services/UserInfoService.cs
@rlorenzo

Copy link
Copy Markdown
Contributor

@JasonRobertFrancis Three threads still open after c18d9c8:

.gitignore:520-521 - .github/skills/impeccable/ and .github/hooks/impeccable.json still ignored.

Viper.csproj:9 - NU1902 still suppressed, which silences NuGet vulnerability advisories project-wide. NU1608 too.

UserInfoService.cs perf - reports-to N+1 is fixed. Still open: PopulateKeysAsync:1053 queries AAUD per key in the loop, GetUserPermissionsForSystemAsync runs 5x returning identical rows because systemPrefix never reaches the SQL, and .AsNoTracking() is absent throughout.

@rlorenzo rlorenzo left a comment

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.

Medium and low items from the same pass, inline.

Comment thread web/Areas/Directory/Services/UserInfoService.cs Outdated
Comment thread web/Classes/SQLContext/KeysContext.cs Outdated
Comment thread web/Controllers/HomeController.cs Outdated
Comment thread test/Usings.cs Outdated
catch (Exception ex) when (ex is DbException || ex is InvalidOperationException)
{
// Exceptions during student info retrieval are caught and ignored to allow other directory details to load.
_logger.LogWarning(ex, "PopulateStudentInfoAsync failed");

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.

@JasonRobertFrancis 27 catch blocks in this file and none rethrow, so a SIS or UCPath outage renders a page that looks complete with sections silently missing. Worth surfacing a partial-data notice.

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.

@JasonRobertFrancis Half done. RecordSectionFailure:298 populates UnavailableSections from 24 call sites and its own comment says "so the view can show a partial-data notice", but nothing ever reads the list. UserInfo.cshtml is the only view bound to UserInfoResult and it never references the property, so a failed section still renders as merely empty. Please render the notice.

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.

@JasonRobertFrancis Still open at d21de551. RecordSectionFailure fills UnavailableSections from 24 call sites and UserInfo.cshtml never reads the property, so a SIS or UCPath outage renders a page that looks complete with sections quietly missing. Instinct failures do reach the list at line 1255, but ErrorMessage itself goes nowhere, so the reason disappears with it. A banner listing Model.UnavailableSections at the top of the view covers both.

Comment thread web/Areas/Directory/Models/UserInfoResult.cs
Comment thread web/Classes/Utilities/IamApi.cs
Comment thread scripts/lint-any.js Outdated
@rlorenzo

Copy link
Copy Markdown
Contributor

@JasonRobertFrancis I was testing this on TEST: https://secure-test.vetmed.ucdavis.edu/2/UserInfo/02725606 and comparing against VIPER1: https://secure-test.vetmed.ucdavis.edu/default.cfm?page=userinfo&id=1000610632&mothraID=02725606

  1. Why do 2 photos render?
  2. The UC Path section is missing "Position: 000652 APPLICATIONS PROGR 4 — 072000 VM: DEANS OFFICE" that is on VIPER1. Also, this must be a data issue, but why is my hire date set to 2/28/2025?
  3. For "System Permissions," VIPER has me with RAPS (52) and SVMSecure (356), but the VIPER2 version has me with RAPS (21), SVMSecure (291), and VIPERForms (1). Why the difference in numbers?

Comment thread .oxlintrc.json Outdated
Comment thread web/wwwroot/css/directory.css Outdated
Comment thread web/Areas/Directory/Services/UserInfoService.cs
Comment thread web/Areas/Directory/Services/UserInfoService.cs
@bsedwards

Copy link
Copy Markdown
Collaborator

@JasonRobertFrancis I was testing this on TEST: https://secure-test.vetmed.ucdavis.edu/2/UserInfo/02725606 and comparing against VIPER1: https://secure-test.vetmed.ucdavis.edu/default.cfm?page=userinfo&id=1000610632&mothraID=02725606

1. Why do 2 photos render?

2. The UC Path section is missing "Position: 000652 APPLICATIONS PROGR 4 — 072000 VM: DEANS OFFICE" that is on VIPER1. Also, this must be a data issue, but why is my hire date set to 2/28/2025?

3. For "System Permissions," VIPER has me with RAPS (52) and SVMSecure (356), but the VIPER2 version has me with RAPS (21), SVMSecure (291), and VIPERForms (1). Why the difference in numbers?

For the effective date, it looks like this is pulling the effective date of the last change to the position. It might make more sense to show the effdt on the job record (the effective date of the last change to the job) in the summary and history views.

Employees have an original hire date, but not a hire date to a specific job.

I would also suggest removing the reports to column from the uc path history. I removed this from the uc path view on the current directory because it can be ambiguous when looking at historical data (which is why it looks like Rex is reporting to Dan).

I checked Rex's permissions on test and the counts look good. The prod user info page is double counting some permissions.

{
}

public virtual DbSet<AbsenceCalendarDV> AbsenceCalendarDVs { get; set; }

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.

@JasonRobertFrancis The four new contexts scaffold 182 entities and the service queries 9 of them: PPS has 134 DbSets and uses VwPeople, VwPersonJobPositionAlls and VwPersonJobPositions; IDCards has 25 and uses IdCards, DvtCardStatuses and DvtReasons; EquipmentLoan has 13 and uses Loans; Keys has 10 and uses Keys and KeyAssignments. That is 10,531 of the 30,584 lines this PR adds, and it pulls in tables no one is querying right now, like UcpathMissingPerson20190821 and UcpathmissingpersonBk. Every one of them now has to track its database by hand. Please cut each context down to what it queries; re-scaffolding a view later takes a minute.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm guessing these tables were pulled in by the EF scaffolding tool (our pre-AI method of automating the creation of context classes). I'm OK with leaving these in because it's likely things will need them in the future, except for tables with names indicating they are a backup or copy, which may or may not exist in production and could probably be deleted from the DB altogether.

};

await _aaudContext.Database.ExecuteSqlRawAsync(
"EXEC AAUD.dbo.usp_get_CurrentOrFutureTermForUser @pidm = @pidm, @loginID = NULL, @termCode = @termCode OUTPUT",

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.

@JasonRobertFrancis UserInfoService makes 10 SqlQueryRaw/ExecuteSqlRawAsync calls against _sisContext and _aaudContext, and both contexts also serve EF entity queries in this same file. CLAUDE.md keeps raw SQL to non-EF tables reached through GetConnectionString(), because mixing the two causes auth failures. The SIS stored procs are a fair reason to reach for raw SQL, so a dedicated context or a plain connection for them would settle it. One more on line 471: EXEC AAUD.dbo.usp_get_CurrentOrFutureTermForUser hardcodes the database name, which ignores whatever database the connection string actually points at.

{
try
{
var ldapUser = LdapService.GetUserByID(result.IamId);

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.

@JasonRobertFrancis PopulateDirectoryInfoAsync calls the synchronous LdapService.GetUserByID on line 315, so every page load parks a thread-pool thread for the LDAP round trip. DirectoryController already does this, so it predates your branch and I am happy for it to go to its own ticket. Raising it because you just batched the N+1 in this request path and this is the blocking call left on it.

<title>@ViewData["Title"] - VIPER(2.0)</title>
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
@if (HttpHelper.HttpContext != null && HttpHelper.HttpContext.Request.Path.ToString().ToLower().Contains("/directory"))
@if (HttpHelper.HttpContext != null && (HttpHelper.HttpContext.Request.Path.ToString().ToLower().Contains("/directory") || HttpHelper.HttpContext.Request.Path.ToString().ToLower().Contains("/userinfo")))

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.

@JasonRobertFrancis Two files carry edits this feature does not need. launchSettings.json flips launchBrowser to false and strips the file's BOM, which diverges from main for everyone who runs the project. This layout renames UserHelper to userHelper in five places and drops its BOM alongside the one change it does need, the /userinfo check that loads directory.css. Every MVC page renders this file, so holding its diff to that css line makes it easier to review now and to revert later.

if (string.IsNullOrWhiteSpace(apiUrl))
{
const string errMsg = "Instinct:ApiUrl is not configured";
_logger.LogWarning("Instinct API: {ErrorMessage}", errMsg);

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.

@JasonRobertFrancis The switch to ILogger reads well. One gap: LogSanitizer shows up once, on line 720, and _logger.LogWarning("Instinct API: {ErrorMessage}", errMsg) here writes a raw upstream HTTP response body to the log. The structured placeholder keeps this out of log-forging range, so it is minor, but sending the Instinct and IAM error strings through SanitizeString() would match the rest of the codebase.

|| new[] { u.MailId, u.LoginId, u.SpridenId, u.Pidm, u.MothraId, u.EmployeeId, u.IamId }
.Any(id => id != null && id.Contains(search)))
.Where(u => u.Current != 0)
.Where(u => u.Current != 0 || u.Future != 0)

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.

@JasonRobertFrancis CmsUserPhotoService.ResolveIdsAsync still filters u.Current != 0, so the future-only users this adds to the results never resolve there. Table.cshtml:41 requests /api/cms/photos/by-mothra/{mothraId}?altPhoto=true, and with no mailId or iamId to fall back on, the miss returns (null, null) and the student gets the generic no-picture placeholder from PhotoService:49 instead of their own photo. Card.cshtml:79 goes through the by-mail route, so the supplied mailId survives the miss and the ID card photo still loads, but iamId stays null and the alternate photo never resolves. Matching that filter to Current != 0 || Future != 0 keeps the two definitions of a directory person in step.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm having a hard time figuring out what the proposed solution is from this text - is it supplying the mail id when looking up a photo?

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.

@bsedwards No, the mail id is a symptom, not the fix. The change is one line in CmsUserPhotoService.ResolveIdsAsync, line 90:

- var query = _aaudContext.AaudUsers.AsNoTracking().Where(u => u.Current != 0);
+ var query = _aaudContext.AaudUsers.AsNoTracking().Where(u => u.Current != 0 || u.Future != 0);

That filter is the photo service's definition of a directory person, and this PR just widened the search's definition to Current != 0 || Future != 0. Until the two match, a future-only student shows up in results and then fails to resolve for their photo.

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.

6 participants