Is there a way to map a Single to another Single if onError? - rx-java2

So the scenario is that I have a user table that may not have any user in it.
#Dao
public interface UserDao {
#Query("SELECT * FROM user LIMIT 1")
Single<UserEntity> getUser();
}
Because of RxRoom, if there's no user, the Single will trigger onError. In this case, I want to map this Single to another Single that pull user from server.
Single<UserEntity> getRemoteUser();
Currently, I'm thinking chain the Singles like this:
userDao.getUser()
.subscribe(
user -> {
Timber.e("found user");
},
throwable -> {
Timber.e(throwable.getMessage());
getRemoteUser().subscribe(....);
});
But I'm wonder is there any better way to achieve this? Maybe something like this:
userDao.getUser()
.mapOnError(throwable -> getRemoteUser())
.subscribe(
user -> {
Timber.e("found user");
},
throwable -> {
Timber.e(throwable.getMessage());
});

Related

How to use gatling feed / foreach methods correctly?

I'm new in gatling/scala
The main idea of my scenario: I need to pass each item from some prepared List of String into the method, and check that response correct
so it's my scenario
object ForeachScenario extends Scenario {
override def profile(): OpenInjectionStep = {
atOnceUsers(3)
}
val rules_new = List("one", "two", "three")
val custom_feed = rules_new.map(el=> Map("expressions_rules" -> el)).iterator
override def createScenario(): ChainBuilder = {
group(name()) {
tryMax(SCENARIO_MAX_RETRIES) {
tryMax(NUMBER_OF_ATTEMPTS) {
// Creating a user and his context
exec(
UserCreator.createUser(
"userName", "userUid"
)
)
}.exitHereIfFailed
.tryMax(NUMBER_OF_ATTEMPTS) {
feed(custom_feed)
.exec(
someClassWithMethod.method("${userName}", "${userUid}", "request-bodies/rulesDS/rules-expressions.json")
.requestBuilder
.check(status.is(200))
.check(jsonPath("$.evaluations[0].result").find.is("100"))
)
}
}
}
}
}
in general, it works as expected, but... I would like to change this code for using only one virtual user, I have some separate scenario for checking functionality related to UserCreator.createUser and in this case, the main task create a user and under his context check another API, so, for this task user creation for each element in my list is redundantly
override def profile(): OpenInjectionStep = {
atOnceUsers(1)
}
I can't understand how can I realize this, because feeder uses only the first item from the list in the case with one virtual user (atOnceUsers(1)), probably I can save somehow userName and UserUid
exec(
UserCreator.createUser(
"userName", "userUid"
)
)
and use them further OR probably have to use another approach for iteration?
can somebody help me with that?

Jetpack Paging3 RemoteMediator returns same PagingSatate on #load append

I'm following this codelab to build an paging3 app with github API and a local DB. While the first 2 pages load fine, the mediator hits a loop when trying to load the 3rd page when scrolling to bottom - the same PagingState is passed to load() function over and over.
Just wondering if anyone knows what could be the possible root cause here?
Some implementation details:
RemoteMediator: (the prevPage and currentPage is from github API's pagination response header and saved to a local DB.)
// RepositoryMediator
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Repository>
): MediatorResult {
return when (loadType) {
LoadType.REFRESH -> {
fireRequestForPage(1, true /*clear DB*/)
return Success(endOfPaginationReached = false)
}
LoadType.APPEND -> {
// !!!!!!! kept getting the same state when APPEND is triggered, resulting in same currentPage and nextPage
// get currentPage, nextPage from state.lastItemOrNull
if(currentPage < nextPage) {
fireRequestForPage(nextPage)
Success(endOfPaginationReached = false)
} else {
return Success(endOfPaginationReached = true)
}
LoadType.PREPEND -> {
// get currentPage, prevPage from state.firstItemOrNull
if(currentPage > prevPage) {
fireRequestForPage(prevPage)
Success(endOfPaginationReached = false)
} else {
return Success(endOfPaginationReached = true)
}
}
}
}
Observable: I'm using liveData instead of flow to from the Pager:
fun searchRepositoryWithUserId(userLoginName: String): LiveData<PagingData<Repository>> {
// need to create a new Pager each time because the search query is different
return Pager(
config = PagingConfig(pageSize = PAGE_SIZE, enablePlaceholders = false),
remoteMediator = RepositoryMediator()
) {
repoDao().getRepositoriesOfUser(userLoginName)
}.liveData
}
Dao: just a plain query
#Query("SELECT * FROM repository_table WHERE login = :ownerLoginName")
fun getRepositoriesOfUser(ownerLoginName: String): PagingSource<Int, Repository>
For anyone interested, the fix is from Dao, need to update the query to sort on reponame, otherwise the query will return the same last Page for PagingSource even if there're new items inserted into DB, confusing the Mediator.
#Query("SELECT * FROM repository_table WHERE login = :ownerLoginName ORDER BY repository_name ASC")
fun getRepositoriesOfUser(ownerLoginName: String): PagingSource<Int, Repository>
Had a similar issue just now. Trying to sort by different fields had led to RemoteMediator getting stuck in a loop on different page numbers.
Turns out I couldn't rely on item ID's assigned by backend to be primary keys for my Room DB Entity. Assigning primary key ID's locally (starting from zero) seems to have fixed the issue.

How to buld a rest ful API with RXJava and Micronaut?

I would like to write a REST API that return HTTP 400 when I try to create an already existing entity or when I try to update an non existing entity.
#Post
fun create(#Body entity: #Valid Entity): HttpResponse<Entity> {
val optional = entityService.find(entity)
if(optional.isPresent) {
return HttpResponse.badRequest()
}
return HttpResponse.created(entityService.save(entity))
}
How can I do that using non blocking endpoint with RXJava2 and Micronaut, I can only find examples with switchIfEmpty
#Post
#Status(HttpStatus.CREATED)
fun createMeal(#Body entity: #Valid Entity): Single<Entity> {
return entityService.find(entity)
.switchIfEmpty(entityService.save(entity))
.map{success -> entity}
}
but this code always return HTTP 200 even if nothing is saved, I don't think it's a good practice.
Thanks
You would use map to convert the entity to a bad request response since if it exists that is what you want to return. You could also use switchIfEmpty to save the entity which would only occur if the entity is not found. Ensure you wrap that code inside Flowable.defer to prevent the logic from executing no matter what. In your reactive example above the save will occur on every execution.
return entityService.find(entity)
.map(entity -> HttpResponse.badRequest())
.switchIfEmpty(Flowable.defer() -> {
//return a publisher that emits HttpResponse.created(entity)
})
Finally I do something like this :
fun update(name: String, entity: Entity): Single<Entity> {
val observable = BehaviorSubject.create<Entity>()
entitysRepository.find(name)
.subscribe(
{
entity.name = name
update(entity, observable)
},
{ observable.onError(RuntimeException("Entity : $name doesn't exist")) }
)
return observable.singleOrError()
}
fun save(entity: Entity): Single<Entity> {
val observable = BehaviorSubject.create<Entity>()
entitysRepository.find(entity.name)
.subscribe(
{ observable.onError(RuntimeException("Entity : ${entity.name} already exist")) },
{ save(entity, observable) }
)
return observable.singleOrError()
}
Don't really know if it's a good practice or not.

Spring Boot: Who to write a custom Prefilter, considering the user role?

I need some kind of #preFilter (or, if it isnot possible than #postFilter) to filter the results of my REST API. I can not use the preFilteranotation, because I need to consider the user role. I have three different roles:
user the normal user, who shold only access data which he owns
teamleader this role should access all data of his team
admin who can access all data.
Because our database structure is really complex, it will be necessary, to access some other data, before I can decide if the user can access the requested data or parts of the requested data.
The snippet works only for the roles user and admin. For teamleader it will be more complex, then there will be a bunch of masterDataId which have to be connect with or.
Here is some pseudocode, hopefully its not to confusing:
public class RoleFilter {
DimensionAttributeValueRepository dimensionAttributeValueRepository;
public void doFilter(Collection<AllDatas> data) {
if (user.getRole() != "admin") {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
DimensionAttributeValue tmpValue = dimensionAttributeValueRepository.findByChrValue(auth.getUsername());
MasterData masterData = tmpValue.getMasterData();
data.filter(data.masterDataId == masterData.getMasterDataID());
}
}
}
Update: Example
Lets say I have two users, user A is a normal user with the role "user". User B is an admin with the role "admin".
There is a Database table, in which the userData are stored. The table looks like the following.
| ID | username | name | email |
Both of them are sending a simple authenticated GET request to /userData.
Now my backend detects based on the authentication header the users and add the roles.
Nwo depending on the role, the user A should only get an answere which contains his personal data, user B should get all data which are accessible though /userData.
Response for user A:
{
"res":[
{
"id":1,
"username":"userA",
"name":"A",
"email":"userA#mail.com"
}
]
}
Response for user B:
{
"res":[
{
"id":1,
"username":"userA",
"name":"A",
"email":"userA#mail.com"
},
{
"id":2,
"username":"userB",
"name":"B",
"email":"userB#mail.com"
},
{
"id":3,
"username":"userC",
"name":"C",
"email":"userC#mail.com"
}
]
}
For your usecase I would recommend to use a custom filter and integrate it into the spring-security filter chain. Here is a tutorial, explaining it in general. You could configure your custom filter so that it checks your complex roles against the database and then overwrite the current users authentication object with a new one.
Example implementation:
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// HERE GOES YOUR CODE
// Depending on the extracted authentication details of the current user, you can now overwrite the users GrantedAuthorities
Collection<SimpleGrantedAuthority> oldAuthorities = (Collection<SimpleGrantedAuthority>)SecurityContextHolder.getContext().getAuthentication().getAuthorities();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_TEAMLEADER");
List<SimpleGrantedAuthority> updatedAuthorities = new ArrayList<SimpleGrantedAuthority>();
updatedAuthorities.add(authority);
updatedAuthorities.addAll(oldAuthorities);
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
SecurityContextHolder.getContext().getAuthentication().getCredentials(),
updatedAuthorities));
chain.doFilter(request, response);
}
}
Afterwards you can check for your roles with this statement: #PreAuthorize("hasRole('ROLE_TEAMLEADER')")
Then you can just access the users roles with the help of the spring-security-context object: SecurityContextHolder.getContext().getAuthentication().getAuthorities()
Based on its results you can now customize your answer upon the roles that are stored in this object. You could for example implement a RestCall on /userData like this:
#GetMapping("/userData")
public List<Object> getUserData() {
List<SimpleGrantedAuthority> roles = (List<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
SimpleGrantedAuthority authorityTeamLeader = new SimpleGrantedAuthority("ROLE_TEAMLEADER");
List<Object> result = new ArrayList<>();
if (roles.contains(authorityTeamLeader)) {
result = getAllUsers();
} else {
result = getPersonalUser(roles);
}
return result;
}

Do some work after the response in ASP.NET Core

I have an ASP.NET Core website, using EFCore.
I would like to do some work like logging to the database, but after having sent the response to the user in order to answer faster.
I could do it in a different thread, but due to async access of the DbContext I am not sure it is safe. Is there any recommended way to do that?
public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
try
{
var newModel = new ResponseViewModel(model);
// Some work
return View("RequestView",newModel)
}
finally
{
// Some analysis on the request
// I would like to defer this part
await Log(model);
}
}
One of the reason is that I would like to call a web-service (geocoding), which is not needed to answer, but good to work on the log (I need the city/country of coordinates).
I see this has never been answered, but actually have a solution.
The simple solution:
public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
try
{
var newModel = new ResponseViewModel(model);
// Some work
return View("RequestView",newModel)
}
finally
{
Response.OnCompleted(async () =>
{
// Do some work here
await Log(model);
});
}
}
The secure solution, as OnCompleted used to be called before the response being sent, so delaying the response:
public static void OnCompleted2(this HttpResponse resp, Func<Task> callback)
{
resp.OnCompleted(() =>
{
Task.Run(() => { try { callback.Invoke(); } catch {} });
return Task.CompletedTask;
});
}
and call Response.OnCompleted2(async () => { /* some async work */ })
Building on Jeans answer and a question and answer on the try - return - finally pattern, the try and finally blocks can be removed (if you don't really want to catch an exception).
This leads to the following code:
public async Task<IActionResult> Request([FromForm] RequestViewModel model, string returnUrl = null)
{
var newModel = new ResponseViewModel(model);
// Some work
Response.OnCompleted(async () =>
{
// Do some work here
await Log(model);
});
return View("RequestView", newModel);
}
There's no out of the box way to do what you want.
But, here's a possible approach:
Have a queue and a worker (thread or process)
Just before the request is sent back to the client, add a message in that queue
The worker will pick up that message at some point in the future, and process it.
Since the worked runs somewhere else and not on the request thread, the server can complete the request thread and the worker can do what's left.
Try using Hangfire. Hangfire is an easy way to perform background processing in .NET and .NET Core applications. No Windows Service or separate process required.
Backed by persistent storage. Open and free for commercial use.
You could do something like
var jobId = BackgroundJob.Enqueue(() => Log(model));
And here is my blog post on using HangFire in ASP.NET Core
Create a new class that inherits from ActionFilterAttribute, overwrite the OnResultExecuted method to perform the logging and then apply your attribute class to the controller actions you want to do logging.