Skip to content

feat: Adds support for filtering and sorting by loginUsername with us…#51

Merged
ustraria merged 7 commits intomainfrom
wip/ustraria/loginusername-filter-sort
Jan 23, 2026
Merged

feat: Adds support for filtering and sorting by loginUsername with us…#51
ustraria merged 7 commits intomainfrom
wip/ustraria/loginusername-filter-sort

Conversation

@ustraria
Copy link
Contributor

@ustraria ustraria commented Jan 14, 2026

…erId fallback

Issue #, if available:

Description of changes:
Added support for filtering and sorting users by loginUsername with fallback to userId when loginUsername is null

Backend changes:
Core filtering and sorting logic:

  • Updated Filter.java to implement proper fallback: check loginUsername first, if null check userId
  • Updated Sort.java to sort by loginUsername when sorting by user Id column, falling back to userId when loginUsername is null

Session template filtering:

  • Added backend logic to resolve loginUsername to userId specifically for session template filtering operations - when filtering session templates by loginUsername (CreatedBy, LastModifiedBy, UsersSharedWith), the backend now looks up the corresponding userId(s) to perform the actual filter
  • Updated API model: Added new filter fields CreatedByLoginUsername, LastModifiedByLoginUsername, and UsersSharedWithLoginUsername to DescribeSessionTemplatesRequestData for loginUsername-based filtering

API model updates:

  • Updated API model: Renamed filter parameters in DescribeUsersRequestData for clarity - UserIds for internal userIds, LoginUsernames for loginUsernames
  • Added usingExternalAuth flag to DescribeUserInfoResponse - set to true when external authentication is configured (checks for presence of loginUsernameKey, displayNameKey, roleKey, or defaultGroupsKey configuration)

Frontend changes:
Session template filtering and display:

  • Updated SessionTemplatesTable to display loginUsername instead of userId for CreatedBy and LastModifiedBy columns
  • Updated useSessionTemplatesService to fetch only users from current page templates (CreatedBy/LastModifiedBy) instead of all users, building a userId→loginUsername map for table display and filter dropdowns
  • Added propertyValueToLabelMaps to FilterBar for displaying loginUsername in filter dropdowns and token chips (used for CreatedBy, LastModifiedBy, and UsersSharedWith filters)
  • Added usingExternalAuth to session and used it to conditionally enable loginUsername-based filtering - when usingExternalAuth is true, user filter keys (CreatedBy, LastModifiedBy, UsersSharedWith) are mapped to their loginUsername equivalents before sending to backend
  • Display loginUsername for CreatedBy/LastModifiedBy in session template details page and split panel
  • Display loginUsername instead of userId in create/edit wizard review step for assigned users

Other

  • Updated SearchUtils to use loginUsername with userId fallback for Users table filter autocomplete
  • Updated user search in AssignUsersGroups.tsx and EditUserGroupForm.tsx to use LoginUsernames filter instead of UserIds
  • Updated filter-users-bar-constants.ts to map LoginUsernames instead of UserIds
  • Fixed pagination reset on sort column change in TableWithPagination
  • Changed default user sort order from DESC to ASC in user selection

Why is this change necessary:
Users are more easily identified by their loginUsername than by userId. Previously, the UI displayed loginUsername in tables, but filtering and sorting still operated on userId, creating a mismatch between what users see and what they can filter/sort by. This change aligns the filtering and sorting behavior with the displayed values, improving usability while maintaining backward compatibility with users who only have userId populated.
How was this change tested:

  • Updated or added new unit tests.
  • Updated or added new integration tests.

Manual Test Cases - Filter and sorting changes
Users Table
Users Table - Filter by User ID (shows loginUsername)

  • Action: Click filter dropdown, select "User ID="
  • Result: Filter input appears populated with users
Screenshot 2026-01-13 at 10 43 27 PM
  • Action: Type a loginUsername value (e.g., "ustraria") and select
  • Result: Users with matching loginUsername shown
Screenshot 2026-01-13 at 10 44 32 PM
  • Action: Also checked "User Id !=", "User Id:" and "User Id!:" filters
  • Result: Filters working and showing correct list of users

Users Table - Sort User ID column

  • Action: Click Users table
  • Result: Default sort is on User ID column in ascending order
Screenshot 2026-01-13 at 11 09 46 PM
  • Action: Click "User ID" column header to sort DESC
  • Result: Users sorted Z->A
Screenshot 2026-01-13 at 11 11 18 PM

Session Templates Table

  • Action: Show "Created by" and "Last modified" by columns
  • Result: These columns display the loginUsername instead of userId
Screenshot 2026-01-13 at 11 14 36 PM

Session Templates Table - Details panel

  • Action: Select a template
  • Result: Created by" and "Last modified" show loginUsername instead of userId
Screenshot 2026-01-20 at 2 17 56 AM

Session Templates Table - Filter by Created By

  • Action: Click filter dropdown, select "Created By"
  • Result: Dropdown shows loginUsername for users from the current page's templates (CreatedBy/LastModifiedBy columns), not all users in the system
Screenshot 2026-01-16 at 7 40 21 AM
  • Action: Type a loginUsername value (e.g., "ustraria") and select
  • Result: Templates created by ustraria shown
Screenshot 2026-01-14 at 11 00 28 AM
  • Action: Also checked "User Id !=", "User Id:" and "User Id!:" filters
  • Result: Filters working and showing correct list of users

Session Templates Table - Filter by Last Modified By

  • Action: Click filter dropdown, select "Last Modified By"
  • Result: Dropdown shows loginUsername for users

Screenshot 2026-01-13 at 11 18 15 PM
  • Action: Also checked "User Id !=", "User Id:" and "User Id!:" filters
  • Result: Filters working and showing correct list of users

Session Templates Table - Filter by Users Shared With

Screenshot 2026-01-14 at 11 03 11 AM
  • Action: Also checked "User Id !=" filter
  • Result: Filters working and showing correct list of users

Create template and assign user

  • Action: Create a new template and assign users during creation
  • Result: Displays login usernames instead of user ids on the review page
Screenshot 2026-01-20 at 2 17 37 AM

Assign Session Templates to Users

  • Action: Session Template → select a template → Assign users and groups and type in "Add users" search box
  • Result: Users filtered, shows loginUsername as label
Screenshot 2026-01-13 at 11 24 49 PM

Add Users to User Group

  • Action: User Groups → select a group → Edit and type in "Add users" search box
  • Result: Users filtered, shows loginUsername as label
Screenshot 2026-01-13 at 11 28 19 PM

Remove Users from User Group

  • Action: User Groups → select a group → Edit and filter by user Id
  • Result: Users filtered
Screenshot 2026-01-13 at 11 29 46 PM

User with no loginUsername

User no_login_name does not have a loginUsername and checked that it was sorted correctly and filterable


Empty filter results

  • Action: Filter by non-existent loginUsername, "user500"
  • Resuls: Shows "No users available"

Multiple filters combined

  • Action: Applied multiple filters on Session Templates (CreatedBy + Name)
  • Result: Expected templates filtered
Screenshot 2026-01-13 at 11 32 23 PM

Also tested filtering and sorting for internal auth setup


Any additional information or context required to review the change:

Documentation Checklist:

  • Updated the README if applicable.
  • Updated the THIRD-PARTY-LICENSES file if applicable.

Compatibility Checklist:

  • I confirm that the change is backwards compatible.
  • There is no modification of dependencies or if there is, a corresponding update to THIRD-PARTY-LICENSES is part of the commit.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 90.29% 80.03%
dcv-access-console-web-client 19.62% 9.24%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.27% 0.14%

@ustraria ustraria marked this pull request as ready for review January 14, 2026 19:04
for (String prop : property) {
Object value = PropertyAccessorFactory.forBeanPropertyAccess(obj).getPropertyValue(prop);
if (value == null) continue;
if (isNegation) return filter(value, filter);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this for the case when loginUsername value does not match but userId does i.e. to support filtering on userId even when loginUsername exists? The customer only sees the loginUsername on the web client if it is present, so we shouldn't need to care.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code is not correct. userId should only be checked if loginUsername is null. I've updated this part in revision.

Object propertyValue1;
Object propertyValue2;

if ("userId".equalsIgnoreCase(sortToken.getKey())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why ignoreCase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok yeah we don't need that we can just match it as it expects as "UserId"

Object propertyValue1;
Object propertyValue2;

if ("userId".equalsIgnoreCase(sortToken.getKey())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's declare the string constants at the top of the class

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

useEffect(() => {
const fetchAllUsers = async () => {
try {
const response = await dataAccessService.describeUsers({} as DescribeUsersRequestData)
Copy link
Contributor

Choose a reason for hiding this comment

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

We're retrieving all users? There are two issues with this:

  1. We don't need all users, we need mapping only for the CreatedBy and LastModified userIds on the current page of SessionTemplates table
  2. We need to paginate through the response to get all the values

We can add this method here

Copy link
Contributor

@mitshiv1905 mitshiv1905 Jan 15, 2026

Choose a reason for hiding this comment

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

In fact, we can call the method as part of the useSessionTemplatesService here instead of using a useEffect

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was getting all users because UsersSharedWith could possibly be any user.
If we only get the users on the current page, the filter would not be right? For example, if all the templates created by user1 are on page 1 and templates by user3 are on page 2 and I want to filter templates created by user3. If only users are fetched that are on the corresponding page we would not be able to filter by user3?

Maybe we could get all users corresponding to CreatedBy, LastModified, and UsersSharedWith and then create the userId -> loginUsername map with just this subset of users instead of all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, updated to fetch only users from the current page's CreatedBy/LastModifiedBy fields instead of all users. This builds a map of userId→loginUsername for:

  1. Displaying loginUsername in table columns (instead of userId)
  2. Populating the filter dropdown with relevant users
  3. Showing loginUsername in filter chips (instead of userId)

}

const fetchFilteringOptions = async (filteringText: string, filteringProperty: string) => {
const labelMap = props.propertyValueToLabelMaps?.get(filteringProperty)
Copy link
Contributor

Choose a reason for hiding this comment

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

Wow what's going on here? Could you please help me understand?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

propertyValueToLabelMaps is passed to FilterBar as a prop from SessionTemplatesTable. This map maps the 3 filtering properties CreatedBy, LastModifiedBy and UsersSharedWith to userIdToLoginUsernameMap which maps userIds to loginUsernames for all the users. This map is created when fetching all users.

So when CreatedBy, LastModifiedBy or UsersSharedWith is selected as the filtering property the map is retrieved and the loop takes the users in the map and makes them dropdown options where value is the userId which is sent to the backend when selected and label is the loginUsername shown in the dropdown.

@ustraria ustraria force-pushed the wip/ustraria/loginusername-filter-sort branch from a758a16 to 1b51244 Compare January 16, 2026 17:05
@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 90.28% 79.85%
dcv-access-console-web-client 19.8% 9.37%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.27% 0.14%

@ustraria ustraria force-pushed the wip/ustraria/loginusername-filter-sort branch from f2ec97c to e14e3c0 Compare January 20, 2026 07:29
@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 90.28% 79.85%
dcv-access-console-web-client 19.8% 9.37%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.26% 0.14%

@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 90.28% 79.85%
dcv-access-console-web-client 19.7% 9.26%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.26% 0.14%

@@ -2029,7 +2029,12 @@ components:
description: "The entity that represents the data that the user passes for describing the users"
properties:
UserIds:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we simplify the filter names? UserIds for internal userIds, LoginUsernames for loginUsernames

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 89.62% 78.65%
dcv-access-console-web-client 19.77% 9.32%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.26% 0.14%

@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 89.62% 78.65%
dcv-access-console-web-client 19.77% 9.32%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.26% 0.14%

@ustraria ustraria requested a review from mitshiv1905 January 21, 2026 19:03
return (await this.getUsersApi()).describeUsers(describeUsersRequest)
}

public async describeUsersByIds(userIds: string[]): Promise<Map<string, string>> {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: name the method more appropriately? This returns the map for provided userIds right?

sessionTemplateId={existingSessionTemplateId!}
handleUsersChange={(users: [OptionDefinition]) => {
let userIds: [string] = []
let labels: string[] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need userIds[]?

import {SESSION_TEMPLATES_TABLE_CONSTANTS} from "@/constants/session-templates-table-constants";

export default function SessionTemplateOverview({sessionTemplate}: { sessionTemplate: SessionTemplate | undefined }) {
type Props = {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: More appropriate naming, follow the pattern for other components and their props

export default function SessionTemplateOverview({sessionTemplate}: { sessionTemplate: SessionTemplate | undefined }) {
type Props = {
sessionTemplate: SessionTemplate | undefined
userDisplayNames?: Map<string, string>
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: These are loginUsernames right?

Copy link
Contributor

@mitshiv1905 mitshiv1905 left a comment

Choose a reason for hiding this comment

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

As discussed offline, let's try to ensure that the number of describeUsers() calls are minimized for SessionTemplate display purposes


DescribeUsersRequestData userRequest = new DescribeUsersRequestData();
userRequest.setLoginUsernames(List.of(new FilterToken().operator(lookupOp).value(filter.getValue())));
List<User> users = userService.describeUsers(userRequest).getUsers();
Copy link
Contributor

@mitshiv1905 mitshiv1905 Jan 22, 2026

Choose a reason for hiding this comment

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

A describeUsers() call per filter is too expensive. Can we group the calls by EQUAL, CONTAINS, NOT_CONTAINS? That way we will only need 3 calls at most.

@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 89.64% 78.58%
dcv-access-console-web-client 19.8% 9.35%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.26% 0.14%

@github-actions
Copy link

Coverage Report

Component Lines Branches
dcv-access-console-handler 90.40% 79.79%
dcv-access-console-web-client 19.8% 9.35%
dcv-access-console-auth-server 58.39% 22.22%
dcv-access-console-configuration-wizard 6.26% 0.14%

Copy link
Contributor

@mitshiv1905 mitshiv1905 left a comment

Choose a reason for hiding this comment

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

In the interest of time, decided to leave the filtering code in DescribeSessionTemplatesController which should ideally be in a separate component or the SessionTemplatesService.

@ustraria ustraria merged commit 2d90b97 into main Jan 23, 2026
3 checks passed
@ustraria ustraria deleted the wip/ustraria/loginusername-filter-sort branch January 27, 2026 00:53
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.

2 participants