I am sure this has been already asked but I can't really describe it and search for it. I hope it gets clear what I try to accomplish from the following code:
userRepository.findAll()
.flatMap(u -> commentRepository.findByUser(u.getId))
.flatMap( // here i get of course the comment object but i also need the previous user object )
You can chain the second flatMap internally with the commentRepository.findByUser(u.getId) and then perform the desired operation inside that internal flatMap
This will look like :
userRepository.findAll()
.flatMap(u -> commentRepository.findByUser(u.getId).flatMap(comment -> {
// operation on comment
// operation on u
// return result
}));
You may also want to return the final result of the whole chain above, in order to maintain the chaining.
You need to use zip, so in next flatMap you will get tupple with item from findAll and then findUser.
userRepository.findAll()
.flatMap(u -> Mono.zip(Mono.just(u), commentRepository.findByUser(u.getId)))
.flatMap(tuple-> tuple.getT1() || tuple.getT2() )
Did you try this?
userRepository.findAll()
.flatMap(u -> {
List<Comment> commentsByUserId = commentRepository.findByUser(u.getId);
commentsByUserId.flatMap(// rest of the code)
// code
})
Related
How to populate the value of this variable:
private val _urlList = MutableLiveData<List<Url>>()
of type Url:
data class Url(
val imgSrcUrl: String
)
with the incoming list of url strings from a firebase call?
Here is where the magic happens:
private fun getData(){
viewModelScope.launch {
try {
getImagesUrl {
"Here where I need to set the value of the variable to a listOf(it) with it being strings
of urls retrieved from firebase storage"
}
}catch (e: Exception){
"Handling the error"
}
}
}
Edit
The map function #dominicoder provided solved my problem, answer accepted.
Thank you all for your help
Your question is unclear because you're showing a live data of a single Url object but asking to stuff it with a list of strings. So first, your live data object needs to change to a list of Urls:
private val _urlList = MutableLiveData<List<Url>>()
Then, assuming getImagesUrl yields a list of strings, if I understood you correctly, then you would map that to a list of Urls:
getImagesUrl { listOfImageUrlStrings ->
_urlList.value = listOfImageUrlStrings.map { imageUrlString -> Url(imageUrlString) }
}
If that does not answer your question, you really need to review it and clarify.
You can set values on the MutableLiveDataObject in two ways (depends on what you're doing).
Setting the value as normal from the UI thread can be done with:
myLiveData.value = myobject
If you're setting it from a background thread like you might in a coroutine with a suspended function or async task etc then use:
myLiveData.postValue(myObject)
It's not clear from your question whether the LiveData is meant to hold a list as you mention both lists and single values. But your LiveData holds a set the values as a collection like a list, set or map. It's can be treated as a whole object so adding a value later needs to have the whole collection set again like:
myLiveData.value = mutableListOf<Url>()
//Response received and object created
myLiveData.value = myLiveData.value.apply {
add(myObject)
}
Or if the value is mutable updating the existing value (preferred as it's cleaner):
myLiveData.value.add(myObject)
The problem with that approach is you're exposing the map as a mutable/writeable object. Allowing accessors to change the values which you might not want.
Tell me how to work with ServerRequest. If I need to get N parameters.
I find simple example with 1 parameter.
Reactive Spring Query Parameters
request
.getQueryParam("type")
.map(type -> service.getAddressByType(type))
.orElseGet(() -> service.getAllAddresses());
You can use getQueryParams to get N parameters as a map.
getQueryParams() returns MultiValueMap, so you can handle query params as a map.
Let me make small example like your code block.
val queryParamsMap = request.queryParams()
queryParamsMap["type"]?.let { type -> service.getAddressByType(type) } ?: let { service.getAllAddresses() }
I have a web method that returns a flux object when it will be time (it's linked to a pub/sub).
Would it at least be possible, only for the first call, to return a default?
public Flux<String> receiveStream() {
return myReactiveService.getData() //here can I return a value at start? //.map(...);
It is not that easy to do it "only for the first call". Each request is supposed to get its own sequence of Strings, unless you take specific steps to change that. And that is at two levels:
- WebFlux: each request leads to a separate invocation of the controller method, so the Flux is newly instantiated
- Reactor: most Flux are "cold", ie they don't generate data until they're subscribed to, and each subscription regenerates a separate dataset.
So even if you returned a cached Flux, it would probably still serve each request separately.
There is a way to share() a long-lived Flux so that later newcomers only see data that becomes available after they've subscribed to the shared Flux, which could help with the "only the first request" aspect of your requirement.
Assuming getData() by itself is cold (ie simply calling it doesn't trigger any meaningful processing):
AtomicReference<Flux<String>> sharedStream = new AtomicReference<>();
public Flux<String> receiveStream() {
Flux<String> result = sharedStream.get();
if (result == null) {
Flux<String> coldVersionWithInit = myReactiveService
.getData()
.startWith(FIRST_VALUE)
.map(...);
Flux<String> hotVersion = coldVersionWithInit.share();
if (sharedStream.compareAndSet(null, hotVersion))
result = hotVersion;
else
result = sharedStream.get();
}
return result;
}
i think what you are looking for is Flux#defaultIfEmpty
Im new to reactive programming paradigm. I have a simple question.
I have a list of elements, for each element i must do a REST call.
I must build a new list based on the response of these calls.
The problem is that my function returns value before the end of the loop...
I don't know how to do ?
Here my piece of code :
TaeaUpdateRequestOutput putTaea(final String dossierId, final TaeaUpdateRequestInput input, final String token) {
final TaeaUpdateRequestOutput output = new TaeaUpdateRequestOutput();
input.getAdhesions().stream().forEach(adhesion -> {
final Mono<TaeaFromMyMB> taeaResponse = doRest(adhesion, TaeaFromMyMB.class, url, token, dossierId);
taeaResponse.subscribe(myMBTaea -> {
final Taea taea = myMBTaea.fromTaeaFromMyMb(adhesion);
output.getListeTaea().add(taea);
});
});
//output is always empty due to async programming. How to wait the the end of the last iteration's subscription?
return output;
}
You subscribed taeaResponsesbut you didn't wait result of the subscription.
Try something like
List<Taea> taeas = Flux.fromIterable(input.getAdhesions())
.flatMap(adhesion -> doRest(adhesion, TaeaFromMyMB.class, url, token, dossierId)
.map(taeaFromMyMB -> taeaFromMyMB.fromTaeaFromMyMb(adhesion)))
.collect(Collectors.toList())
.subscribeOn(Schedulers.parallel())
// wait result here
.block();
Then set it to the output.
Keep in mind that block() waits indefinitely, so you can use reactor.core.publisher.Mono#block(java.time.Duration) to avoid it.
I'm having an issue when trying to return an Option result from aka-http.
Basically it's a get that might have a 404.
pathPrefix("contacts" / Segment) { id =>
get {
contactService.getById(id).map {
case Some(c: ContactDto) => complete(OK -> toResource(c))
case None => complete(HttpResponse(NotFound))
}
}
}
Which gives me and error of:
[error] found : scala.concurrent.Future[akka.http.scaladsl.server.StandardRoute]
[error] required: akka.http.scaladsl.server.Route
[error] (which expands to) akka.http.scaladsl.server.RequestContext => scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
[error] contactService.getById(id).map {
Any help would be greatly appreciated.
The problem you are seeing here has to do with the fact that you are using a Future and not because of the Option. I'm going to assume that the call contactService.getById(id) returns a Future. As the result of any route within your routing tree needs to be a Route (short for RequestContext => Future[RouteResult]) and your Future is itself not a Route, then you need to make a small change to handle this situation. You should be able to use the onComplete directive in combination with your Future as follows:
pathPrefix("contacts" / Segment) { id =>
get {
val fut = contactService.getById(id)
onComplete(fut){
case util.Success(Some(c: ContactDto)) =>
complete(OK -> toResource(c))
case util.Success(None) =>
complete(HttpResponse(NotFound))
case util.Failure(ex) =>
complete(HttpResponse(InternalServerError))
}
}
}
This code now handles the 3 possible outcomes from the Future (success with a Some, success with a None and a failure), producing a Route for each of those cases. This should fix your problem.
#cmbaxter's answer is correct but if you're happy with the standard status codes for the three cases above (Ok, NotFound, InternalServerError) then you can simplify the code to just complete directly with your function that returns Future[Option[T]].
pathPrefix("contacts" / Segment) { id =>
get {
complete(contactService.getById(id).map(toResource))
}
}
That is assuming that toResource returns a type where a ToEntityMarshaller exists for the type returned by that function. Akka provdes the machinery for Future and Option so you just need to supply the T part. For example if you were returning json and using spray-json then you can define a JsonWriter[T] and the implicits in akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport will do the rest. See spray-json-support.
The map(toResource) may not actually be required, but I'm assuming that does additional conversion of ContactDto to some other type - if its just converting it to json or similar then you can drop it and use the in built marshalling support as described above.