Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIPFU-77 - Add 'User assignment status' filter group #249

Merged
merged 11 commits into from
Jan 16, 2024
Merged

Conversation

Terala-Priyanka
Copy link
Contributor

@Terala-Priyanka Terala-Priyanka commented Jan 10, 2024

Purpose

UIPFU-77 - Add "User assignment status" filter in find-user-plugin

Approach

The requirement is to create a new filter group in this plugin.

  1. This filter group should be conditionally rendered in this plugin i.e., ATM from user settings permission set detail only.
  2. This filter group is implemented differently than any regular filter group.
  3. Existing functionality remains same.

How is it different?
The new filters do not participate in cql formation.
The cql that could be formed by this filter group could not be supported by BE API for various other challenges and design reasons. Hence an alternative mechanism is chosen to populate the users based on "Assigned" and "Unassinged" filters.

  1. assigned and unassigned filter strings are omitted to include in cql formation.
  2. when assigned filter is selected alone or in combination with other filter groups, no cql will is formed and no API call is made, instead list of users is rendered based on initialSelectedUsers.
  3. when unassigned filter is selected, filter string is altered by removing 'unassigned' filter string and add others based on the scenario in context. The results of fetch users api is filtered not to include initial selected users.

Unit tests

We will gradually migrate unit tests in this module to Jest/RTL .
For the new files, Jest minimal setup has been introduced and unit tests have been written.
image

Screencast

DFcQyHnddK.mp4

Pre-Merge Checklist

Before merging this PR, please go through the following list and take appropriate actions.

  • I've added appropriate record to the CHANGELOG.md
  • Does this PR meet or exceed the expected quality standards?
    • Code coverage on new code is 80% or greater
    • Duplications on new code is 3% or less
    • There are no major code smells or security issues
  • Does this introduce breaking changes?
    • If any API-related changes - okapi interfaces and permissions are reviewed/changed correspondingly
    • There are no breaking changes in this PR.

If there are breaking changes, please STOP and consider the following:

  • What other modules will these changes impact?
  • Do JIRAs exist to update the impacted modules?
    • If not, please create them
    • Do they contain the appropriate level of detail? Which endpoints/schemas changed, etc.
    • Do they have all they appropriate links to blocked/related issues?
  • Are the JIRAs under active development?
    • If not, contact the project's PO and make sure they're aware of the urgency.
  • Do PRs exist for these changes?
    • If so, have they been approved?

Ideally all of the PRs involved in breaking changes would be merged in the same day to avoid breaking the folio-testing environment. Communication is paramount if that is to be achieved, especially as the number of intermodule and inter-team dependencies increase.

While it's helpful for reviewers to help identify potential problems, ensuring that it's safe to merge is ultimately the responsibility of the PR assignee.

@Terala-Priyanka Terala-Priyanka marked this pull request as draft January 10, 2024 07:53
Copy link

github-actions bot commented Jan 10, 2024

Jest Unit Test Statistics

0 tests  ±0   0 ✔️ ±0   0s ⏱️ ±0s
0 suites ±0   0 💤 ±0 
0 files   ±0   0 ±0 

Results for commit 4bd8c31. ± Comparison against base commit 1e3f510.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Jan 10, 2024

BigTest Unit Test Statistics

17 tests  ±0   17 ✔️ ±0   0s ⏱️ ±0s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ±0 

Results for commit 4bd8c31. ± Comparison against base commit 1e3f510.

♻️ This comment has been updated with latest results.

@Terala-Priyanka Terala-Priyanka requested a review from a team January 10, 2024 12:57
@@ -32,6 +34,28 @@ const compileQuery = template(
{ interpolate: /%{([\s\S]+?)}/g }
);

export function buildQuery(queryParams, pathComponents, resourceData, logger, props) {
const filters = props.initialSelectedUsers ? filterConfigWithUserAssignedStatus : filterConfig;
const updatedResourceData = props.initialSelectedUsers && resourceData?.query?.filters?.substring(`${UAS}`) ? updateResourceData(resourceData) : resourceData;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure about the usage of substring here. Is this method from String? If so, then it only accepts startIndex and endIndex

src/utils.js Outdated
* When UnAssigned filter is selected incombination with any other filters(in other filter groups),
* filter astring for Unassigned is removed and th erest of the filter string is propagated to makeQueryFunction.
*/
const alteredfilters = newRData.query.filters.split(',').filter(str => !str.startsWith(`${UAS}`)).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

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

Won't startsWith(`${UAS}`) remove uas.Assigned too? In case when there are filters selected from other groups and both Assigned and Unassigned are checked?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

uas.Assigned should be removed in any case, as it will never participate in cql formation. So here the logic is appropriate. I see a need to update the comment above this code of line. Thank you.

src/utils.js Outdated
/*
* When Assigned filter is selected on 'User assigbment Status' filter group, in any combination of filters in other filter groups,
* cql formation is not needed.
* hence remove aus filter before propagating it further to makeQueryFunction
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably needs to be uas instead of aus

src/utils.js Outdated
} else if (filterString.includes(`${UNASSIGNED_FILTER_KEY}`)) {
/*
* When UnAssigned filter is selected incombination with any other filters(in other filter groups),
* filter astring for Unassigned is removed and th erest of the filter string is propagated to makeQueryFunction.
Copy link
Contributor

Choose a reason for hiding this comment

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

A few typos here

src/utils.js Outdated
newRData.query.filters = alteredfilters;
} else if (filterString.includes(`${ASSIGNED_FILTER_KEY}`)) {
/*
* When Assigned filter is selected on 'User assigbment Status' filter group, in any combination of filters in other filter groups,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand this case. If there 100 assigned users, and we need to see only staff members for example - can we not do this?

Copy link
Contributor Author

@Terala-Priyanka Terala-Priyanka Jan 11, 2024

Choose a reason for hiding this comment

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

For any combination (from other filter groups) with "Assigned" filter, there is no need to fetch the users once again(as the users list is already available through initialSelectedUsers prop) and hence we don't need any filters to participate in cql formation.
getUsersBasedOnAssignmentStatus does the needful.

src/utils.js Outdated
};

const filterUsersList = (filterString, initialSelectedUsers, users, filterCheck) => {
let usersList;
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 make it an empty array by default to prevent any exceptions

src/utils.js Outdated
let usersList;
if (filterString === `${ASSIGNED}`) {
const assignedUsers = Object.values(initialSelectedUsers);
if (filterCheck) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can give filterCheck argument a default value () => true to remove this if else

Copy link
Contributor Author

Choose a reason for hiding this comment

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

filterCheck argument is not always needed. Function overloading is used here.
image

src/utils.js Outdated
} else if (filterString === `${UNASSIGNED}`) {
// when ONLY "Unassigned" filter is selected
const assignedUserIds = Object.keys(initialSelectedUsers);
if (filterCheck) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here regarding filterCheck

src/utils.js Outdated
return usersList;
};

// eslint-disable-next-line consistent-return

This comment was marked as outdated.

const activeFilterState = { uas: ['Assigned'] };
const uasFilterValue = ['Assigned'];
const initialSelectedUsers = {
'7daa365a-d8c1-4e5d-90ac-ab38f8230827': {

This comment was marked as outdated.

@BogdanDenis BogdanDenis requested a review from a team January 10, 2024 15:27
src/utils.js Outdated

// eslint-disable-next-line consistent-return
export const getUsersBasedOnAssignmentStatus = (activeFilterState, uasFilterValue, initialSelectedUsers, users) => {
const condForOneOfTheFilters = (u) => activeFilterState?.active?.includes(u.active ? `${ACTIVE}` : `${INACTIVE}`) || activeFilterState?.pg?.includes(u.patronGroup);

This comment was marked as outdated.

@Terala-Priyanka Terala-Priyanka marked this pull request as ready for review January 11, 2024 06:47
@Terala-Priyanka Terala-Priyanka requested review from BogdanDenis and a team January 11, 2024 12:16
}
const uasFilterValue = activeFilters.split(',').filter(f => f.includes(`${UAS}`))[0].split('.')[1];

let otherFilterGroups = activeFilters.split(',').filter(f => !f.includes(`${UAS}`)).map(f => f.split('.')[0]);
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need check other filter values because this result list is alredy returned from back-end for a search with these filters. We just need to select from fetched users assigned or not assigned based on uas filter value

if (activeFilters.includes(`${UAS}`)) {
      const assignedUserIds = Object.keys(initialSelectedUsers);
      
      const uasFilterValues = activeFilters.split(',').filter(f => f.find(`${UAS}`));
      
      // both or neither Assignment status values are selected, so we can return result from BE
      if (uasFilterValues.length === 2 || uasFilterValues.length === 0) {
        return fetchedUsers;
      }
      
      if (uasFilterValues.includes(ASSIGNED_FILTER_KEY)) {
        return fetchedUsers.filter(user => assignedUserIds.includes(user.id));
      } else {
        return fetchedUsers.filter(user => !assignedUserIds.includes(user.id));
      }
}

return fetchedUsers;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you again for your review!
I agree that it is not needed for Unassigned filter as we are propagating the filters to make cql. But I think it is needed in case of Assigned filter as we are not propagating the filter string to make the cql(as we don't actually need to fetch)

@Terala-Priyanka Terala-Priyanka requested review from BogdanDenis and a team January 11, 2024 16:04
Copy link
Contributor

@artem-blazhko artem-blazhko left a comment

Choose a reason for hiding this comment

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

Could you cover new functionality in UserSearchContainer.js file by jest/RTL tests?

@@ -32,6 +39,28 @@ const compileQuery = template(
{ interpolate: /%{([\s\S]+?)}/g }
);

export function buildQuery(queryParams, pathComponents, resourceData, logger, props) {
const filters = props.initialSelectedUsers ? filterConfigWithUserAssignedStatus : filterConfig;
const updatedResourceData = props.initialSelectedUsers && resourceData?.query?.filters?.includes(`${UAS}`) ? updateResourceData(resourceData) : resourceData;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const updatedResourceData = props.initialSelectedUsers && resourceData?.query?.filters?.includes(`${UAS}`) ? updateResourceData(resourceData) : resourceData;
const updatedResourceData = props.initialSelectedUsers && resourceData?.query?.filters?.includes(UAS) ? updateResourceData(resourceData) : resourceData;

const activeFilters = get(resources, 'query.filters', '');
const assignedUsers = Object.values(initialSelectedUsers);

if (activeFilters === `${ASSIGNED_FILTER_KEY}`) return assignedUsers;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (activeFilters === `${ASSIGNED_FILTER_KEY}`) return assignedUsers;
if (activeFilters === ASSIGNED_FILTER_KEY) return assignedUsers;


if (activeFilters === `${ASSIGNED_FILTER_KEY}`) return assignedUsers;

if (activeFilters.includes(`${UAS}`)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (activeFilters.includes(`${UAS}`)) {
if (activeFilters.includes(UAS)) {


if (activeFilters.includes(`${UAS}`)) {
const assignedUserIds = Object.keys(initialSelectedUsers);
const hasBothUASFilters = activeFilters.includes(`${ASSIGNED_FILTER_KEY}`) && activeFilters.includes(`${UNASSIGNED_FILTER_KEY}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const hasBothUASFilters = activeFilters.includes(`${ASSIGNED_FILTER_KEY}`) && activeFilters.includes(`${UNASSIGNED_FILTER_KEY}`);
const hasBothUASFilters = activeFilters.includes(ASSIGNED_FILTER_KEY) && activeFilters.includes(UNASSIGNED_FILTER_KEY);

Comment on lines 198 to 199
const hasNoneOfUASFilters = !activeFilters.includes(`${ASSIGNED_FILTER_KEY}`) && !activeFilters.includes(`${UNASSIGNED_FILTER_KEY}`);
const uasFilterValue = activeFilters.split(',').filter(f => f.includes(`${UAS}`))[0].split('.')[1];
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const hasNoneOfUASFilters = !activeFilters.includes(`${ASSIGNED_FILTER_KEY}`) && !activeFilters.includes(`${UNASSIGNED_FILTER_KEY}`);
const uasFilterValue = activeFilters.split(',').filter(f => f.includes(`${UAS}`))[0].split('.')[1];
const hasNoneOfUASFilters = !activeFilters.includes(ASSIGNED_FILTER_KEY) && !activeFilters.includes(UNASSIGNED_FILTER_KEY);
const uasFilterValue = activeFilters.split(',').filter(f => f.includes(UAS))[0].split('.')[1];

return fetchedUsers;
}

if (uasFilterValue === `${ASSIGNED}`) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (uasFilterValue === `${ASSIGNED}`) {
if (uasFilterValue === ASSIGNED) {

@@ -168,6 +181,35 @@ class UserSearchContainer extends React.Component {
return get(this.props.resources, 'query', {});
}

getUsers = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest to move this function outside the component. I will much easier to test all if...else conditions.
But it is up to you.

src/utils.js Outdated
export const updateResourceData = (rData) => {
const filterString = rData?.query?.filters;
const newRData = cloneDeep(rData);
if (filterString === `${UNASSIGNED_FILTER_KEY}` || filterString === `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}` || filterString === `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (filterString === `${UNASSIGNED_FILTER_KEY}` || filterString === `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}` || filterString === `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`) {
if (filterString === UNASSIGNED_FILTER_KEY || filterString === `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}` || filterString === `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`) {

src/utils.js Outdated
const alteredfilters = 'active.active,active.inactive';
newRData.query.filters = alteredfilters;
} else {
const alteredfilters = newRData.query.filters.split(',').filter(str => !str.startsWith(`${UAS}`)).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const alteredfilters = newRData.query.filters.split(',').filter(str => !str.startsWith(`${UAS}`)).join(',');
const alteredfilters = newRData.query.filters.split(',').filter(str => !str.startsWith(UAS)).join(',');


describe('updatedResourceData', () => {
describe('when only UnAssigned filter is selected', () => {
[`${UNASSIGNED_FILTER_KEY}`, `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}`, `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`].forEach(filterStr => (
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
[`${UNASSIGNED_FILTER_KEY}`, `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}`, `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`].forEach(filterStr => (
[UNASSIGNED_FILTER_KEY, `${ASSIGNED_FILTER_KEY},${UNASSIGNED_FILTER_KEY}`, `${UNASSIGNED_FILTER_KEY},${ASSIGNED_FILTER_KEY}`].forEach(filterStr => (

@artem-blazhko artem-blazhko requested a review from a team January 12, 2024 11:48
@Terala-Priyanka
Copy link
Contributor Author

Could you cover new functionality in UserSearchContainer.js file by jest/RTL tests?

Hey @artem-blazhko
We will soon start working on migrating to Jest/RTL. So this file will be covered in the scope of that ticket that is there in our backlog.

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions

0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarCloud

@Terala-Priyanka Terala-Priyanka merged commit 32f0730 into master Jan 16, 2024
5 of 6 checks passed
@Terala-Priyanka Terala-Priyanka deleted the UIPFU-77 branch January 16, 2024 05:49
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.

3 participants