Skip to content

Conversation

@Dmitriy-Litvinenko
Copy link
Contributor

@Dmitriy-Litvinenko Dmitriy-Litvinenko commented Nov 7, 2025

Purpose

We're in the process of anonymizing requests, like how loans have been. Previously if user data was not returned for a request it was caused by something weird but now it indicates the request has been anonymized (probably).

Approach

There were many disparate places that were handling the display and logic around requesters. I consolidated all the ways to create a link to a user/barcode into a component as well as all the ways to display the requester/proxy. Once these were all consistent I changed the text/link behavior for when a user does not exist to Anonymized. I'm going to be updating the request list in another PR to better display anonymized requests which will now be a easier because of the consistency.

I'm not super happy with the amount of duplication with the React PropTypes but I'm not a React developer and don't know how this is usually deduplicated.

Refs

https://folio-org.atlassian.net/browse/UIREQ-1313
Corresponding Backend PR: folio-org/mod-circulation#1623

Screenshots

To test this I nulled out the requester in the database.

image image

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.

Notes:

Origin pull request was #1330 but we was not able run Sonar for fork and we created current pull.

@Dmitriy-Litvinenko Dmitriy-Litvinenko changed the title UI req 1313 UIREQ-1313: Display Anonymized Requesters Nov 7, 2025
@github-actions
Copy link

github-actions bot commented Nov 7, 2025

Jest Unit Test Results

    1 files  ± 0     68 suites  +2   1m 45s ⏱️ +5s
  856 tests +11    856 ✅ +11  0 💤 ±0  0 ❌ ±0 
1 221 runs  +11  1 221 ✅ +11  0 💤 ±0  0 ❌ ±0 

Results for commit d9f2eb7. ± Comparison against base commit 37b4890.

This pull request removes 9 and adds 20 tests. Note that renamed tests count towards both.
RequesterLink When barcode is not presented should trigger NoValue component ‑ RequesterLink When barcode is not presented should trigger NoValue component
RequesterLink When barcode is presented should render `Link` with correct `href` attribute ‑ RequesterLink When barcode is presented should render `Link` with correct `href` attribute
RequesterLink When barcode is presented should render `Link` with correct label ‑ RequesterLink When barcode is presented should render `Link` with correct label
UserDetail when all data provided should trigger "userHighlightBox" with correct arguments ‑ UserDetail when all data provided should trigger "userHighlightBox" with correct arguments
UserForm When user id is not provided should trigger "userHighlightBox" with requester id ‑ UserForm When user id is not provided should trigger "userHighlightBox" with requester id
UserForm When user id is provided should trigger "userHighlightBox" with user id ‑ UserForm When user id is provided should trigger "userHighlightBox" with user id
createUserHighlightBoxLink returns a link given values ‑ createUserHighlightBoxLink returns a link given values
createUserHighlightBoxLink returns empty string given no values ‑ createUserHighlightBoxLink returns empty string given no values
formatter Only required data should call "RequesterLink" with correct props ‑ formatter Only required data should call "RequesterLink" with correct props
BarcodeLink When barcode is not presented should trigger NoValue component ‑ BarcodeLink When barcode is not presented should trigger NoValue component
BarcodeLink When barcode is presented and the requester does not have an id should render `Link` with correct `href` attribute ‑ BarcodeLink When barcode is presented and the requester does not have an id should render `Link` with correct `href` attribute
BarcodeLink When barcode is presented and the requester has an id should render `Link` with correct `href` attribute ‑ BarcodeLink When barcode is presented and the requester has an id should render `Link` with correct `href` attribute
BarcodeLink When barcode is presented should render `Link` with correct label ‑ BarcodeLink When barcode is presented should render `Link` with correct label
FullNameLink When requester is not presented and the requesterId does not exist should render anonymized ‑ FullNameLink When requester is not presented and the requesterId does not exist should render anonymized
FullNameLink When requester is not presented and the requesterId exists should render unknown ‑ FullNameLink When requester is not presented and the requesterId exists should render unknown
FullNameLink When requester is present and the requester does not have an id should render `Link` with correct `href` attribute ‑ FullNameLink When requester is present and the requester does not have an id should render `Link` with correct `href` attribute
FullNameLink When requester is present and the requester has an id should render `Link` with correct `href` attribute ‑ FullNameLink When requester is present and the requester has an id should render `Link` with correct `href` attribute
FullNameLink When requester is present should render `Link` with correct label ‑ FullNameLink When requester is present should render `Link` with correct label
UserDetail when all data provided should trigger "UserHighlightBox" with correct arguments ‑ UserDetail when all data provided should trigger "UserHighlightBox" with correct arguments
…

♻️ This comment has been updated with latest results.

@zburke
Copy link
Member

zburke commented Nov 10, 2025

@Dmitriy-Litvinenko , @brycekbargar

Origin pull request was #1330 but we was not able run Sonar for fork and we created current pull.

FYI see these instructions for how to merge the original PR from the fork. GitHub will recognize matching commit hashes across the two PRs, updating the checks on the original PR (where they originally failed due to lack of access to secrets) to reflect the status on the derived PR (where presumably they pass). In the past, we have generally merged the original PR in an effort to best-preserve authorship, though @brycekbargar will be listed here as a co-author.

Copy link
Member

@zburke zburke left a comment

Choose a reason for hiding this comment

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

There is a lot of great work here, but I think it needs a bit of clean-up and documentation before it is ready to merge.

The rock this PR is built on is that undefined user-data represents "unknown" whereas null user-data represents "anonymized". Please document this approach in the PR description, or link to a wiki page or a jira ticket or ... something that describes how anonymized data can be recognized from data that is simply missing or corrupt.

As you noted, display logic related to users/proxies was spread around and duplicated. I like the way you have consolidated it here. Nice job!

describe('BarcodeLink', () => {
describe('When barcode is presented', () => {
let mockedRequester;
let mockedRequesterId;
Copy link
Member

Choose a reason for hiding this comment

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

If this never gets reassigned, use const instead of assigning it in the beforeEach.

});

describe('FullNameLink', () => {
let mockedRequesterId;
Copy link
Member

Choose a reason for hiding this comment

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

If this never gets reassigned, use const instead of assigning it in the beforeEach.

userId,
user,
}) => {
if (user != null) {
Copy link
Member

Choose a reason for hiding this comment

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

Conditionals are safer when they are shaped like "if (things I need) { stuff() } else { error-or-default }". Here, that means if (user).

Comment on lines 16 to 18
return userId != null
? <FormattedMessage id="ui-requests.errors.user.unknown" />
: <FormattedMessage id="ui-requests.requestMeta.anonymized" />;
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, be inclusive not exclusive. I would expect Sonar to complain here. It prefers positive conditions where there is an else. IOW, "if (condition) { thing } else { default }" is better than "if (not condition) { default } else { thing }".
Also, use strict comparisons (=== or !==) instead of abstract operators (== or !=) to avoid unexpected type conversions. Include a comment when you are distinguishing among different kinds of falsey values. null vs undefined is a subtle difference, so any help you can give to somebody debugging this code in the future is helpful.
Here, null means anonymized and undefined means unknown. Just say that :) In fact, consider putting it in a header-comment up before line 8 that describes how this component treats all the different props it can receive.

@@ -1 +1 @@
import React from 'react';
Copy link
Member

Choose a reason for hiding this comment

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

You don't need to import React itself; we're on React v18.2 and this import has been automatically included since v17. There's no harm, but in new code you might as well omit it.

@Dmitriy-Litvinenko Dmitriy-Litvinenko requested a review from a team November 11, 2025 10:45
@brycekbargar
Copy link

@zburke

There is a lot of great work here, but I think it needs a bit of clean-up and documentation before it is ready to merge.

The rock this PR is built on is that undefined user-data represents "unknown" whereas null user-data represents "anonymized". Please document this approach in the PR description, or link to a wiki page or a jira ticket or ... something that describes how anonymized data can be recognized from data that is simply missing or corrupt.

The assumptions have been added to the UIREQ-1313 JIRA ticket as well as the https://folio-org.atlassian.net/wiki/spaces/DD/pages/1056210996/Request+anonymization technical design.

@Dmitriy-Litvinenko
Copy link
Contributor Author

Hello. @brycekbargar Thank you for your pull request. We left several comment on them. Could you please take a look on them when you will have time?

@Dmitriy-Litvinenko
Copy link
Contributor Author

Hello. @folio-org/fe-tl-reviewers Could you please take a look when you will have time?

@sonarqubecloud
Copy link

</Row>
);
export function isUserAnonymized(userId) {
return !userId;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you please check here for equal to null?
return userId === null;

just for example we also can add

/**
 * Checks if the user is anonymized.
 * A user is considered anonymized if the userId is null.
 *
 * @param {*} userId - The user identifier to check.
 * @returns {boolean} True if userId is null, otherwise false.
 */
describe('isUserAnonymized', () => {
  it('should returns true when userId is null', () => {
    expect(isUserAnonymized(null)).toBe(true);
  });

  it('should returns false when userId is a string', () => {
    expect(isUserAnonymized('abc123')).toBe(false);
  });

  it('should returns false when userId is undefined', () => {
    expect(isUserAnonymized(undefined)).toBe(false);
  });

  it('should returns false when userId is a number', () => {
    expect(isUserAnonymized(123)).toBe(false);
  });

  it('should returns false when userId is an empty string', () => {
    expect(isUserAnonymized('')).toBe(false);
  });
});

Choose a reason for hiding this comment

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

Is there any reason to check for null specifically instead of both null and undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hello, as far as I understand from the description and explanations, for "anonymous" users, the "id" will only be changed to "null."
The case where the user's "id" is "undefined" does not apply to "anonymous" users.

@Dmitriy-Litvinenko
Copy link
Contributor Author

Duplicate for #1348

@Dmitriy-Litvinenko Dmitriy-Litvinenko deleted the UIREQ-1313 branch January 14, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants