Skip to content

fix issues with Void return type due to rxjs map operator null checks#236

Closed
David-Development wants to merge 2 commits intomainfrom
fix-void-types
Closed

fix issues with Void return type due to rxjs map operator null checks#236
David-Development wants to merge 2 commits intomainfrom
fix-void-types

Conversation

@David-Development
Copy link
Member

No description provided.

@desperateCoder
Copy link
Contributor

This one will still die (in most cases my code comes across this line...):

return (T) nextcloudAPI.performRequestObservableV2(typeArgument, request).map(r -> r.getResponse());

.setMethod("GET")
.setUrl(mApiEndpoint + "callWithNoReturnType")
.build();
verify(nextcloudApiMock).performRequestV2(eq(retrofit2.Call.class), eq(request));
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't this just verify the call is made? The problem is returning the async result, so I'm not sure if this a viable test... Does this test break if you bring the map() back?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that is indeed a problem. The data is coming from a mock and therefore the observable always returns an empty observable. If we want to add more checks I think we'd have to add dummy objects for all types.. right?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think a mocked test isn't the way to go here, at least we are mocking the wrong objects here... The mock would need to be much deeper, somewhere at the NC Files app interface I guess. We need a mock which instantly triggers the callbacks to see, if anything dies in this chain, but I didn't dig in this far yet. I'll have a look!

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, the tests here are mostly to check that the type and url was extracted correctly and that the interface was called. However return types are not considered. Thank you for looking into it!

@nextcloud-android-bot
Copy link
Collaborator

Codacy

32

Lint

TypemasterPR
Warnings33
Errors00

SpotBugs (new)

Warning TypeNumber
Bad practice Warnings2
Correctness Warnings15
Internationalization Warnings2
Multithreaded correctness Warnings3
Performance Warnings5
Security Warnings5
Dodgy code Warnings9
Total41

SpotBugs (master)

Warning TypeNumber
Bad practice Warnings2
Correctness Warnings17
Internationalization Warnings2
Multithreaded correctness Warnings3
Performance Warnings5
Security Warnings5
Dodgy code Warnings10
Total44

try (Reader targetReader = new InputStreamReader(responseStream)) {
if (targetEntity != Void.class) {
return gson.fromJson(targetReader, targetEntity);
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a safe ClassCastException, since we are casting the result... Correct me if I'm wrong, but i think you cast the Object somewhere to Void, because I definitely don't. I used to get, what I defined I want to get, so the casting is done SSO-Internally, see comment below...

Copy link
Member Author

Choose a reason for hiding this comment

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

Ugh.. me being naive while thinking that I could just throw in some typescript-style code 🤷‍♂️ 😅 I think you might be right about the casting thing. However it would be interesting to see if it actually fails 🙈

Copy link
Contributor

Choose a reason for hiding this comment

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

No need to be curious here, i mean look: you have a check above, if the target entity ( = T) is NOT a Void. Then, in else the first thing you do is casting a new Object() to T ( = Void). One thing I can tell you for sure: Object.class != Void.class. Game over. Thats why I was going for NOTHING, since this is at least semi-safe, as long as the caller specifies a return type. Otherwise the worst thing that could happen is, when the return type is specified, but the server doesn't return anything (=null), then we would get the error from the mapper.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm yes, I get that point. I think I'm also good with the nothing option then. However I'm just thinking about other case. So if you specify List without a type, it'll be interpreted as Void as well, right? But what if I'm too lazy as a programmer to write the type but I still want to somehow receive my response object..? Maybe I'm overthinking it a little here..

Copy link
Contributor

Choose a reason for hiding this comment

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

So if you specify List without a type, it'll be interpreted as Void as well, right? But what if I'm too lazy as a programmer to write the type but I still want to somehow receive my response object..?

Nope, would be a List<Object> you could cast, if your're brave enough to guess the actual type. Would work in this case. The problem with your solution is only the void type, which is broken here. So we're back at the beginning of our problem 🤣. I'll have another look at it, maybe this weekend and see what I can do about this.

} else if(this.returnType == Observable.class) {
// Observables without any type information (see above for Observables with type info)
return (T) nextcloudAPI.performRequestObservableV2(Void.class, request); //.map(r -> r.getResponse());
return (T) nextcloudAPI.performRequestObservableV2(Void.class, request).map(r -> r.getResponse());
Copy link
Contributor

Choose a reason for hiding this comment

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

here we go, here is the ClassCastException going to happen

@David-Development
Copy link
Member Author

@desperateCoder Did you have a chance to fight the unit tests yet? 🙈 Or do you prefer that we close this pr here and create a new issue instead?

@desperateCoder
Copy link
Contributor

@David-Development Actually not, since @stefan-niedermann keeps throwing feature requests at my head... If you say you really need my help, I'll see what I can do, although I hate testing 😅

@AndyScherzinger
Copy link
Member

@David-Development @desperateCoder @stefan-niedermann I guess this PR is obsolete given the lately merged new approach brought in by Stefan via #542

@stefan-niedermann
Copy link
Member

obsolete from my PoV

@AndyScherzinger AndyScherzinger deleted the fix-void-types branch October 20, 2025 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2. developing Work in progress

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants