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

[Nullability Annotations to Java Classes] Add Missing Nullability Annotations to Comment Network Client Classes (safe) #2868

Merged

Conversation

ParaskP7
Copy link
Contributor

@ParaskP7 ParaskP7 commented Oct 11, 2023

Parent #2798

This PR adds any missing nullability annotation to CommentRestClient.java and CommentXMLRPCClient.java, all its related response classes and anything in between (ie. CommentStore). See response classes below:

FYI.1: This change is safe, meaning that there shouldn't be any (major) compile-time changes associated with this change. Having said that, testing is highly recommended since the changes in this PR can affect one or more clients (ie. JP/WPAndroid, WCAndroid, etc)

FYI.2: An analysis on CommentStore and its usages with our main clients suggests that this store is only being utilizing by JP/WPAndroid. AFAIA WCAndroid is not using that store and as such can be safely ignored from testing.

PS: Note that CommentStore.java and their respective network client classes, both REST and XMLRPC, seem to be deprecated. The CommentsStore.kt and their respective network client classes, both REST and XMLRPC, seem to be the ones that are actually being actively used atm. Also note that the CommentWPComRestResponse.java response is shared between those two stores. As such, it seems that all comment related API calls from JP/WPAndroid are currently flowing via the new CommentsStore.kt, while only FluxC is using the deprecated CommentStore.java. Be mindful of all that when testing this PR. 🤷


@wzieba let me know how I did on this PR, that is, in terms of updating these network client classes and adding nullability annotations on them and their main CommentWPComRestResponse response class, in order to utilize the nullability annotations added on its fields and thus make its use null proof. If you also feel that this is a good way to proceed with such client PRs, I'll then use this pattern across the board and follow a similar approach on every such PR.


Nullability Annotation List:

  1. Add missing nn-a to likes utils pvd field on cmt rest client
  2. Add missing n-a to fetch comments on comment clients
  3. Add missing n-a to push comment on comment clients
  4. Add missing n-a to fetch comment on comment clients
  5. Add missing n-a to fetch comment likes on comment rest client
  6. Add missing n-a to delete comment on comment clients
  7. Add missing n-a to create new reply/comment on comment clients
  8. Add missing n-a to like comment on comment rest client
  9. Add missing n-a to comments response to comment list
  10. Add missing nl-a to requested status field on payload
  11. Add missing n-a to comment response to comment
  12. Add missing n-a to get cmt status from xmlrpc status string
  13. Add missing n-a to comments response to comment list
  14. Add missing n-a to comment response to comment
  15. Add missing n-a to comment wp com rest response fields

Nullability Checks List:

  1. Remove unnecessary payload likes if condition on comment store

Warnings Resolutions List:

  1. Create missing switch branch on comment xmlrpc client
  2. Remove complexity from get cmt status from xmlrpc status str
  3. Replace explicit type argument with <> on payload
  4. Replace if with switch on comment xmlrpc client
  5. Replace explicit type argument with <> on payload
  6. Make inner classes static on comment wp com rest response

Warnings Suppression List:

  1. Suppress long parameter list warning on comments clients
  2. Suppress too many functions warning on comments xmlrpc client
  3. Suppress unused warnings on comment store
  4. Suppress unused return value warning on comment store
  5. Suppress raw types warning on comment store

Refactor List:

  1. Reformat comment rest client
  2. Reformat comment xmlrpc client
  3. Replace anonymous classes with lambda on comment rest client
  4. Replace anonymous classes with lambda on comment xmlrpc client
  5. Remove test only comment store method

Test List:

  1. Suppress new class naming convention for connected tests
  2. Remove interrupted exception from test method's throws list
  3. Simplify assert null assertion for connected tests
  4. Resolve robolectric related application deprecation warnings
  5. Guard usages of get comment by site and remote id method
  6. Simplify assert null assertion for unit tests
  7. Remove unnecessary comment response local variable from unit test
  8. Fix typo with comments response on unit test
  9. Suppress unchecked cast on unit test
  10. Fix typo with updated comment on unit test

Associated Clients

It is highly recommending that this change is being tested on the below associated clients:


To Test (REST):

  • Smoke test the FluxC Example app via the COMMENTS screen. Verify everything is working as expected.
  • In addition to the above, using local-builds.gradle on JP/WPAndroid, smoke test any CommentStore related functionality on both, the WordPress and Jetpack apps, and see if everything is working as expected. For a couple of examples, you can expand and follow the inner and more explicit test steps within:

To Test (XMLRPC):

  • Create a new self-hosted WordPress site for XMLRPC testing purposes (jurassic.ninja).
  • Smoke test the FluxC Example app via the COMMENTS screen. Verify everything is working as expected.
  • In addition to the above, using local-builds.gradle on JP/WPAndroid, smoke test any CommentStore related functionality on both, the WordPress and Jetpack apps, and see if everything is working as expected. For a couple of examples, you can expand and follow the inner and more explicit test steps within:
1. Comment Details Screen from Comments Screen [CommentDetailFragment.java]

ℹ️ This test applies to both, the Jetpack and WordPress app.

  • Go to Comments screen and tap on any comment.
  • Verify that the Comment Details screen is shown and functioning as expected.
  • For example, try replying, approving, marking as spam, liking, trashing, editing or copying a comment's link address. Additionally, make sure to verify that you can still swipe left-right to navigate between comments.
2. Comment Details Screen from Notification Tab [CommentDetailFragment.java]

ℹ️ This test applies to the Jetpack app only.

  • Go to Notifications tab, then its COMMENTS sub-tab, and tap on any comment.
  • Verify that the Comment Details screen is shown and functioning as expected.
  • For example, try replying to a comment. Additionally, make sure to verify that you can still swipe left-right to navigate between comments.
3. Edit Comment Screen from Comments Screen [UnifiedCommentsEditActivity.java]

ℹ️ This test applies to both, the Jetpack and WordPress app.

  • Go to Comments screen and tap on any comment.
  • From the More on the right side, at the bottom of the comment, select Edit.
  • Verify that the Edit Comment screen is shown and functioning as expected.
  • For example, try editing/adding the comment's Name, Web address, Email address and its Comment itself.
4. Edit Comment Screen from Notification Tab [UnifiedCommentsEditActivity.java]

ℹ️ This test applies to the Jetpack app only.

  • Go to Notifications tab, then its COMMENTS sub-tab, and tap on any comment.
  • From the More on the right side, at the bottom of the comment, select Edit.
  • Verify that the Edit Comment screen is shown and functioning as expected.
  • For example, try editing/adding the comment's Name, Web address, Email address and its Comment itself.
5. Reader Comments Screen [ReaderCommentListActivity.java]

ℹ️ This test applies to the Jetpack app only.

  • Go to Reader tab, then its DISCOVER sub-tab, and tap on any post.
  • Tap on the Comments button at the bottom of the post.
  • Verify that the Reader Comments screen is shown and functioning as expected.
  • For example, try (un)following, enabling/disabling in-app notifications, replying to post, and then sharing, replying or liking a specific comment.

@ParaskP7 ParaskP7 self-assigned this Oct 11, 2023
@ParaskP7 ParaskP7 force-pushed the analysis/add-missing-nullability-annotations-to-comment-clients branch from 8219631 to 645d3a5 Compare October 11, 2023 15:21
@ParaskP7 ParaskP7 changed the title [Nullability Annotations to Java Classes] Add Missing Nullability Annotations to Comment Client Classes (safe) [Nullability Annotations to Java Classes] Add Missing Nullability Annotations to Comment Network Client Classes (safe) Oct 12, 2023
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.

------------------------------------------------------------------------

You will notice that I added extra conditions for when to trigger
'createNewComment(...)' versus 'createNewReply(...)' versus just
throwing an illegal state exception for any other condition.

if (payload.post != null && payload.reply == null) {
    ...
} else if (payload.reply != null && payload.post == null) {
    ...
} else {
    throw IllegalStateException(...)
}

I did that because the two 'RemoteCreateCommentPayload' constructors are
also defined as such and thus the callers should also respect their
contract in order to avoid any NPE or an illegal app state. I also chose
to keep the 'payload.reply == null' condition and add the extra
'payload.post == null' condition because the 'payload.reply == null'
condition was already present.
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'nl-a' stands for 'nullable annotation'.
Warning: "Explicit type argument CommentModel can be replaced with <>"
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
Warning: "'if' statement can be replaced with 'switch' statement"
FYI: 'n-a' stands for 'nullability annotations'.
FYI: 'n-a' stands for 'nullability annotations'.
Both those payload constructors are actually meant to and used by client
apps (ie. JP/WPAndroid). They just seem unused as they are not being
directly used via the example app or any unit/ui tests within this repo.
Warning: "Raw use of parameterized class 'Action'"
The 'instantiateCommentModel(...)' related 'CommentStore' methods is
being in production and are only being used on a couple of connected
tests. Thus, it is better to remove those and not have them exposed and
available for clients like JP/WPAndroid to exploit.

PS: This change was done to assist with a future refactor and in order
to avoid extra testing.
Warning: "Condition 'payload.likes != null' is always 'true'"
Warning: "Explicit type argument LikeModel can be replaced with <>"
Warnings:
- "Test class name 'ReleaseStack_CommentTestWPCom' doesn't match
regex '[A-Z][A-Za-z\d]*Test(s|Case)?|Test[A-Z][A-Za-z\d]*|IT(.*)|(.*)
IT(Case)?'"
- "Test class name 'ReleaseStack_CommentTestXMLRPCCom' doesn't match
regex '[A-Z][A-Za-z\d]*Test(s|Case)?|Test[A-Z][A-Za-z\d]*|IT(.*)|(.*)
IT(Case)?'"
Warnings: "Exception 'java.lang.InterruptedException' is never thrown in
the method"
Warning: "'assertEquals()' can be simplified to 'assertNull()'"
Warning: "'application' is deprecated"

Explanation: Accessing this field directly is inherently incompatible
with 'org.robolectric.annotation.experimental.LazyApplication' and
Robolectric makes no guarantees if a test modifies this field during
execution.

Replacing 'application' with 'getApplication()' resolves this
deprecation warning.
Warning: "'assertEquals()' can be simplified to 'assertNull()'"
Warning: "Variable 'commentResponse' is never used"
Warning: "Typo: In word 'Reponse'"
Warnings:
- "Unchecked cast: Class<*>! to Class<XMLRPCRequest>"
- "Unchecked cast: Any! to Response<Any>"
Warnings: "Typo: In word 'udpated'"
Warnings:
- "Inner class 'CommentsWPComRestResponse' may be 'static'"
- "Inner class 'Post' may be 'static'"
- "Inner class 'Author' may be 'static'"
FYI: 'n-a' stands for 'nullability annotations'.

PS: This 'NotNullFieldNotInitialized' warning got suppressed because a
'response' class can never have its fields initialized via a constructor
initialization, or otherwise for that matter.
Copy link
Contributor

@wzieba wzieba left a comment

Choose a reason for hiding this comment

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

I haven't finished review yet, leaving some comments to start discussion though.

Comment on lines +697 to +699
throw IllegalStateException(
"Either post or reply must be not null and both can't be not null at the same time!"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

This will likely crash client apps in production if this scenario happens - clients don't have possibility to handle this exception. Can we change it to a log and failed OnCommentChange event?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👋 @wzieba and thanks for this comment, this is indeed an interesting one, let me explain:

  1. First, you can take a look at this commit's description: 53e14a0
  2. From there, you will notice that I did that because the two RemoteCreateCommentPayload constructors are also defined as such and thus the callers should also respect their contract in order to avoid any NPE or an illegal app state. I also chose to keep the payload.reply == null condition and add the extra payload.post == null condition because the payload.reply == null condition was already present.
  3. So, all in all, we should never reach this else case and thus if we do it is end-up being an illegal state of the app. Instead of trying to hide this state and somehow mute it I think it is better to throw an exception, that is, if and whenever someone changes how the RemoteCreateCommentPayload is constructed, because if they don't, as it is now, it will never happen anyway.

Does that makes sense? 🙏

Copy link
Contributor

Choose a reason for hiding this comment

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

the two RemoteCreateCommentPayload constructors are also defined as such and thus the callers should also respect their contract in order to avoid any NPE or an illegal app state

Thanks. When writing this comment, I was under the impression that RemoteCreateCommentPayload is a mapped response from the API. But then I looked at WordPress-Android codebase and understood, that it's a request created via constructor. Let's keep the exception, then!

}
}

private long getPrioritizedRemoteCommentId(@NonNull RemoteCommentPayload payload) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the motivation behind this method? Are we sure it's valid to use remoteCommentId if comment is null? It seems to change a logic of many methods.

Copy link
Contributor Author

@ParaskP7 ParaskP7 Oct 17, 2023

Choose a reason for hiding this comment

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

First all all, thanks for this comment @wzieba ! 💯

As discussed, this was done to avoid duplication on having the same logic with the REST and XMLRPC clients themselves, and also risking it be on one of the clients and not the other.

FYI:

  1. First such commit, which creates this extra method: 9f6c87b
  2. Second such commit, which reused this extra method: 12fe3cf
  3. There are move such commits following the same pattern above.

@ParaskP7
Copy link
Contributor Author

👋 @wzieba and thanks for starting the review and any discussion on the changes, looking forward for us to finish the review! 🙇 ❤️

authorProfileImageUrl = if (allowNulls) null else "https://gravatar.com/avatar/111222333",
postTitle = if (allowNulls) null else "again",
remoteSiteId = 100_000L,
authorUrl = if (allowNulls) "" else "https://test-debug-site.wordpress.com",
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the motivation behind assigning empty string to these fields instead of null? null is a valid value for many fields in CommentEntity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question @wzieba ! 👍

Briefly, this is done because otherwise this test suite will fail due to the fact that a some fields on the CommentWPComRestResponse are now defined as @NonNull. Let me explain:

  1. You can try and change authorUrl = if (withEmpty) "" else "https://test-debug-site.wordpress.com", to authorUrl = if (withEmpty) null else "https://test-debug-site.wordpress.com", and see one test failing.
  2. Now, in order to fix that you would need to update URL = entity.authorUrl ?: "" to URL = entity.authorUrl ?: null.
  3. However, you will quickly notice that this is not compiling due to the fact that Author.URL is defined as @NonNull on CommentWPComRestResponse.

Does that make sense? 🙏

Also, this 'SameParameterValue' warning got suppressed for the
'getDefaultCommentList(...)' function as it is currently being used once
and as 'getDefaultCommentList(false)'. How this doesn't mean it will not
be used in the future, or wasn't used in the past. As such, keeping this
method signature and suppressing this warning for the time being seems
to be the better approach here.

Related PR Comment: https://github.com/wordpress-mobile/
WordPress-FluxC-Android/pull/2868#discussion_r1362018044
Copy link
Contributor

@wzieba wzieba left a comment

Choose a reason for hiding this comment

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

Thanks for answering the questions @ParaskP7 , looks good to me! I've tested the change on the FluxC example and Jetpack apps and haven't spotted any crashes/problems.

@ParaskP7
Copy link
Contributor Author

Awesome @wzieba , thanks for reviewing, testing and raising all those questions, you rock! 🙇 ❤️ 🚀

@ParaskP7 ParaskP7 merged commit 6eee26f into trunk Oct 18, 2023
2 checks passed
@ParaskP7 ParaskP7 deleted the analysis/add-missing-nullability-annotations-to-comment-clients branch October 18, 2023 11:37
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.

2 participants