CompletableFuture<ResponseEntity> Status Code Expectations - spring

So, I have created a Controller within which I have a POST endpoint like so:
#PostMapping("/foo/{some-field}")
public CompletableFuture<ResponseEntity> foo() {
//Do some operations...
...
if(doesNotExist({some-field})) {
return CompletableFuture.completedFuture(ResponseEntity.notFound().build());
}
return CompletableFuture.completedFuture(ResponseEntity.ok().build());
}
Now I would expect that if doesNotExist({some-field}) == true, I'd be prompted with a NOT_FOUND status.
I however end up with a OK status every time around.
Are my expectations wrong in regards to how the ResponseEntity is returned?
Any suggestions how to get the NOT_FOUND status if doesNotExist({some-field}) == true would be much appreciated.
Edit/Update
From the comments I assume my initial question was a little to light, so let me explain when I see this failing, as it seems that my assumption of what the ResponseEntity.HttpStatus would be is correct.
I have made small adjustments to the code block above.
The situation where I receive an unexpected status is when I try to test the NOT_FOUND situation through Spring Cloud Contracts.
An example of the contract looks as follows:
Contract.make {
request {
method 'POST'
url "/foo/SomeNoneExistingField"
body("{}")
headers {
contentType applicationJson()
}
}
response {
status HttpStatus.NOT_FOUND.value()
}
}
So the {some-field} in this contract is set to a field which ensure that doesNotExist({some-field}) == true. I see it end up in this block if I am debugging my code as well.
Nonetheless, the Spring Cloud Contract test status that the response.status == OK i.o. NOT_FOUND.
Might I be using Spring Cloud Contracts incorrectly if my assumption on the HttpStatus returned from a CompletableFuture is correct?
Any help/advice is (again) much appreciated.

There is nothing complex here and it should work as expected.
It is happening may be because of {some-state} is not true so every time it is going to else block.
Ensure that {some-state} evaluation returns true and compiler enters into if block.
if({some-state}) {
return CompletableFuture.completedFuture(ResponseEntity.notFound().build());
}

Ok, I figured out the issue I was experiencing.
Credits to #Marcin Grzejszczak for putting me on the right track in regards to configuration.
What I was missing from my contracts to be able to handle async results, like a CompletableFuture, was that I needed to add async() to my result.
Thus, a contract like so:
Contract.make {
request {
method 'POST'
url "/foo/SomeNoneExistingField"
body("{}")
headers {
contentType applicationJson()
}
}
response {
status HttpStatus.NOT_FOUND.value()
async() // <---- This was it!
}
}
Did the trick.

Related

How to mock webclient in Kotlin and spring boot for unit tests with mockk framework?

I have the following piece of code in Kotlin (using WebFlux), which I wanna test:
fun checkUser(user: People.User?): Mono<Unit> =
if (user==null) {
Mono.empty()
} else {
webClient.get().uri {
uriBuilder -> uriBuilder
//... building a URI
}.retrieve().bodyToMono(UserValidationResponse::class.java)
.doOnError {
//log something
}.map {
if (!item.isUserValid()) {
throw InvalidUserException()
}
}
}
My unit test so far looks like this:
#Test
fun `Returns error when user is invalid`() {
val user = People.User("name", "lastname", "street", "zip code")
//when
StepVerifier.create(checkUser(user))
//then
.expectError(InvalidUserException::class.java)
.verify()
}
However when I run it, it throw the following error:
io.mockk.MockKException: no answer found for: WebClient(#1).get()
at io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:90)
at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:42)
at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16)
at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:263)
at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:25)
at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:20)
I guess the error occurs because I havent mocked WebClient(#1).get() but I am not sure how to mock it. So far I have tried:
every { webClient.get() } returns WebClient.RequestHeadersUriSpec
but it doesnt compile. The error says:
Classifier 'RequestHeadersUriSpec' does not have a companion object, and thus must be initialized here
Someone knows how I can mock WebClient(#1).get()? Thanks in advance
Basically you need something like this:
mock ResponseSpec - mock the body or error in whichever way you need for the respective test case
mock RequestHeadersUriSpec - let the retrieve() method return the ResponseSpec mock
mock WebClient - let the get() method return the RequestHeadersUriSpec mock
Here is a full example:
val response = mockk<WebClient.ResponseSpec>()
val spec = mockk<WebClient.RequestHeadersUriSpec<*>>()
val client = mockk<WebClient>()
every { response.bodyToMono(String::class.java) } returns Mono.just("Hello StackOverflow")
every { spec.retrieve() } returns response
every { client.get() } returns spec
println(client.get().retrieve().bodyToMono(String::class.java).block())
This will correctly print the Hello StackOverflow string.
Though it may be a "historical" question, I actually also had this problem recently.
Just as what Krause mentioned, the full call path of WebClient should be mocked. This means the method stream in every{} block should as the same as WebClient call. In your case, it may be something like
every{webClient.get().uri {???}.retrieve().bodyToMono(???)} returns Mono.just(...)
The next question is something about the error message io.mockk.MockKException: no answer found for: RequestBodyUriSpec(#3).uri(......). The key to the question is methods with parameters and without parameters are totally different things.
Thus, for target method, a uri(Function<UriBuilder, URI> uriFunction) is called(a lambda expression is used here to instead of Function interface). However, for mock method, a uri() method without any parameter is called. This is why the error message said , "no answer found for ...". Therefore, in order to match the mocked method, the code should be:
every{webClient.get().uri(any<java.util.function.Function<UriBuilder, URI>>()).retrieve().bodyToMono(???)} returns Mono.just(...)
Or, the any() method can be changed to the real URI which should be as the same as the target method.
Similarly, bodyToMono() should also be mocked with the correct parameter, which may be bodyToMono(any<ParameterizedTypeReference<*>>()).
Finally, the mock code may look like:
every{client.get()
.uri(any<java.util.function.Function<UriBuilder, URI>>())
.retrieve().bodyToMono(any<ParameterizedTypeReference<*>>())}
return Mono.just(...)

Can an Owin Middleware return a response earlier than the Invoke method returns?

I have the following middleware code:
public class UoWMiddleware : OwinMiddleware
{
readonly IUoW uow;
public UoWMiddleware(OwinMiddleware next, IUoW uow) : base(next)
{
this.uow = uow;
}
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch
{
uow.RollBack();
throw;
}
finally
{
if (uow.Status == Base.SharedDomain.UoWStatus.Running)
{
var response = context.Response;
if (response.StatusCode < 400)
{
Thread.Sleep(1000);
uow.Commit();
}
else
uow.RollBack();
}
}
}
}
Occasionally we observe that the response returns to client before calling uow.Commit() via fiddler. For example we put a break point to uow.Commit and we see the response is returned to client despite that we are on the breakpoint waiting. This is somewhat unexpected. I would think the response will strictly return after the Invoke method ends. Am I missing something?
In Owin/Katana the response body (and, of course, the headers) are sent to the client at the precise moment when a middleware calls Write on the Response object of the IOwinContext.
This means that if your next middleware is writing the response body your client will receive it before your server-side code returns from the call to await Next.Invoke().
That's how Owin is designed, and depends on the fact that the Response stream may be written just once in a single Request/Response life-cycle.
Looking at your code, I can't see any major problem in such behavior, because you are simply reading the response headers after the response is written to the stream, and thus not altering it.
If, instead, you require to alter the response written by your next middleware, or you strictly need to write the response after you execute further logic server-side, then your only option is to buffer the response body into a memory stream, and than copy it into the real response stream (as per this answer) when you are ready.
I have successfully tested this approach in a different use case (but sharing the same concept) that you may find looking at this answer: https://stackoverflow.com/a/36755639/3670737
Reference:
Changing the response object from OWIN Middleware

GoogleUser.getAuthResponse() doesn't contain access_token

UPDATE2: I revisited this issue and have solved the problem by carefully following the doco linked below. But first, for those who are struggling with this, you are in good company. There are so many versions of the doco from Google it is confusing! Do you include platform.js or client.js in your html? Do you load gapi.auth or gapi.auth2? Do you use gapi.auth2.render or gapi.auth.authorize, or gapi.auth2.init, and so on.
The way that returns an access_token (as of this article date) is linked below. I managed to get this working by carefully following the guide and reference using platform.js. Other libraries are then dynamically loaded such as client.js using gapi.load('drive', callback).
https://developers.google.com/identity/sign-in/web/listeners
https://developers.google.com/identity/sign-in/web/reference
==== ORIGINAL ISSUE FOR PROSPERITY ====
UPDATE 1:
I've updated the code sample to do a recursive search of the googleUser object. At least this shouldn't break in a subsequent library.
Below is a code snippet to handle an issue where the access_token in the Google gapi.auth2.AuthResponse object is not at the top level... it is hidden :( in the depths of the object!
So it is retrievable, but not at the top level!!?? I've noticed it seems to be a timing issue... once the application is running for a while on subsequent checks, it does contain the access token at the top level!!
var authResponse = _.googleUser.getAuthResponse();
_.id_token = authResponse.id_token; // Always exists
// access_token should also be a param of authResponse
if (authResponse.access_token) {
debug("Worked this time?");
_.access_token = authResponse.access_token;
} else {
// !!! Internal object access !!!
debug("Attempt to get access token from base object.");
_.access_token = _.objRecursiveSearch("access_token", _.googleUser);
if (_.access_token) {
debug("Access token wasn't on authResponse but was on the base object, WTF?");
} else {
debug("Unable to retrieve access token.");
return false;
}
}
_.objRecursiveSearch = function(_for, _in) {
var r;
for (var p in _in) {
if (p === _for) {
return _in[p];
}
if (typeof _in[p] === 'object') {
if ((r = _.objRecursiveSearch(_for, _in[p])) !== null) {
return r;
}
}
}
return null;
}
I'm guessing getAuthResponse somehow provides a callback once it is ready, but I can't see where in the API.
https://developers.google.com/identity/sign-in/web/reference
I know this question is fairly old, but it appears first when googling for ".getAuthResponse() doesn't have access_token," which is how I got here.
So for those of you in 2016 (and maybe later) here's what I have found out
There's a secret argument on .getAuthResponse, not documented anywhere I have found. If you would run the following in your app
console.log(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse);
You would see that you get the following (copy/pasted from my console)
function (a){if(a)return this.hg;a=.HE;var c=.rf(this.hg);!a.Ph||a.dL||a.Lg||(delete c.access_token,delete c.scope);return c}
This shows that the .getAuthResponse() function looks for an argument, and as far as I can tell doesn't even check its value -- it simply checks if it is there and then returns the whole object. Without that function, the rest of the code runs and we can see very clearly it is deleting two keys: access_token and scope.
Now, if we call this function with and without the argument, we can check the difference in the output. (note: I used JSON.stringify because trying to copy/paste the object from my browser console was causing me some issues).
console.log(JSON.stringify(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse()));
console.log(JSON.stringify(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse(true)));
getAuthResponse() object
{
"token_type":"Bearer",
"login_hint":"<Huge mess of letters>",
"expires_in":2112,
"id_token":"<insert your ridiculously long string here>",...}
getAuthResponse(true) object
{
"token_type":"Bearer",
"access_token":"<an actual access token goes here>",
"scope":"<whatever scopes you have authorized>",
"login_hint":"<another mess of letters>",
"expires_in":2112,
"id_token":"<Insert your ridiculously long string here>",
...}
Figured out the fix for this. Turns out that if we don't provide the login scope config in gapi.auth2.init it doesn't return access_token in getAuthResponse. Please call gapi.auth2.init as given below and access_token will be present.
gapi.auth2.init({
client_id: <googleClientID>,
'scope': 'https://www.googleapis.com/auth/plus.login'
})

Returning error from SignalR server method

I am new to SignalR and there is a small detail I can't get my head around.
My SignalR hub include many channels and the clients can join one or many of these channels via a server method:
joinChannel(string channelName)
What I don't understand is what this method should return.
If it were a normal "RPC" method I would return a status (200 - Ok, 404 - Not found, 403 - Forbidden etc) via IHttpActionResult.
How do I indicate success/failure in SignalR?
What determines if the reply gets to .done or .fail in the client?
Update
Currently my method returns a non-zero value in case of error.
int joinChannel(string channelName) {
...
return errorCode;
}
This works but it create unnecessarily complicated code in the client
hubProxy.server.joinChannel('channel1')
.done(function (result) {
if (result != 0) {
// error handling
}
})
.fail(function (error) {
// error handling
});
To confirm that your action was successfully performed, you can have a client method call. So, basically it would look like this:
public void ServerMethod(argumentList)
{
if (/* server code executed successfully */)
Clients.Caller.onSuccess(arguments);
else Clients.Caller.onFailure(arguments);
}
What this piece of code does is to notify the caller of the server method of a success/failure by calling a client method - method defined in JavaScript. You can also have a method executed on All clients, or only on specific users.
Since it is not an RPC mechanism, I think this is the closest thing you can do to simulate a return type in SignalR.
Hope this helps!
Best of luck!
What about
HubException in
Microsoft.AspNetCore.SignalR.
This is available in ASP.NET Core.
This exception is thrown on the server and sent to client. You can also derive from that class to put your own information in it.

My WebAPI2 service returns a JSON string, but $resource does not parse it

Here is my resource:
var app, deps;
deps = ['ngGrid', 'getUsers'];
angular.module('getUsers', ['ngResource'])
.factory('users', function ($resource)
{
return $resource('/Admin/GetUsers', {}, {
query: { method: 'GET', IsArray: true }
});
});
and then I've added code to try to add a step to force parsing:
$scope.myData = users.query(function(response)
{
if (typeof (response) == string)
{
response = JSON.parse(response);
}
});
But it never gets this far, and here's the error in Chrome:
Error: [$resource:badcfg] object
http://errors.angularjs.org/1.2.14/$resource/badcfg?p0=array
at http://localhost:23002/Scripts/angular.js:78:12
at a.module.factory.f.(anonymous function).p.then.m.$resolved (http://localhost:23002/Scripts/angular-resource.min.js:8:517)
at deferred.promise.then.wrappedCallback (http://localhost:23002/Scripts/angular.js:11046:81)
at deferred.promise.then.wrappedCallback (http://localhost:23002/Scripts/angular.js:11046:81)
at http://localhost:23002/Scripts/angular.js:11132:26
at Scope.$get.Scope.$eval (http://localhost:23002/Scripts/angular.js:12075:28)
at Scope.$get.Scope.$digest (http://localhost:23002/Scripts/angular.js:11903:31)
at Scope.$get.Scope.$apply (http://localhost:23002/Scripts/angular.js:12179:24)
at done (http://localhost:23002/Scripts/angular.js:7939:45)
at completeRequest (http://localhost:23002/Scripts/angular.js:8142:7)
Of course I searched for that error, but I found advice to set IsArray to true or false, this makes no difference. If I set a breakpoint and call JSON.parse on the response string, it gets turned into an array of objects, exactly like what I want. So the string is perfectly valid JSON, but angular appears unwilling to parse it as such, it accepts it as a string and then dies.
My controller is very simple:
public List<ApplicationUser> GetUsers()
{
return AdminUsersViewModel.AllUsers;
}
and then, that method uses a LINQ query to get users from the DB, and then iterates over that collection to create a new one, because before I did that, it just blew up. I've made the call in the browser, and see the same string that is appearing in the angular code.
What I need to know is, why isn't angular spotting that this is a collection of objects, and how can I either force it to parse the string, or change the format so angular can tell what it is ?
Thanks for looking.
OK, I got it. I thought the help I read said to use IsArray ( in hindsight, I think it said DON'T use IsArray ). I knew that was non standard, but that's what I did. I ran the non minified version, and found my error. Change to isArray, and it works.
Thanks for looking :-)

Resources