Reputation: 3671
I have a reactive web-application (Spring WebFlux) where I am storing user profile image through a POST API in Azure storage. I need to create a storage bucket for the user if it doesn't exist.
The service layer method looks something like this: (which contains nested flatMaps - which is considered a bad practice in Reactive apps and is a code smell.) I want to refactor out such that there is no nested flatMap. Is there any way to do this?
public Mono<BlockBlobItem> processAndUpload(FilePart filePart) {
return Mono.subscriberContext().flatMap(context -> {
String userId = context.get(USER_ID_CONTEXT);
BlobContainerAsyncClient blobContainerClient = blobServiceAsyncClient.getBlobContainerAsyncClient(userId);
return blobContainerClient.exists()
.doOnEach(logOnNext(doesContainerExist -> {
if (doesContainerExist) {
LOGGER.info(STORAGE_CONTAINER_EXISTS_MSG);
} else {
LOGGER.info(STORAGE_CONTAINER_DOES_NOT_EXIST_MSG);
}
}))
.doOnEach(logOnError(err -> LOGGER.error(CONTAINER_CHECK_FAILURE_MSG, err.getMessage(), err)))
.flatMap(doesContainerExist -> {
if (doesContainerExist) {
return uploadFile(filePart, blobContainerClient, userId);
} else {
return blobContainerClient.createWithResponse(null, null)
.doOnEach(logOnComplete(response -> LOGGER.info(CONTAINER_CREATION_SUCCESS_MSG)))
.doOnEach(logOnError(err -> LOGGER.error(CONTAINER_CREATION_FAILURE_MSG, err.getMessage(), err)))
.then(uploadFile(filePart, blobContainerClient, userId));
}
});
});
}
Upvotes: 1
Views: 958
Reputation: 44368
I am afraid the nested flatMap
is the only way to go as long as you need the both contexts.
If you speak about refactoring, I'd move the lambda expressions (or their right sides at least) out of the method to achieve readability. Also consider use map
first to get the userId
as long as you don't need the initial context
.
public Mono<BlockBlobItem> processAndUpload(FilePart filePart) {
return Mono.subscriberContext()
.map(context -> context.get(USER_ID_CONTEXT))
.flatMap(userId -> {
var client = blobServiceAsyncClient.getBlobContainerAsyncClient(userId);
return client.exists()
.doOnEach(logOnNext(doesContainerExistLambda))
.doOnEach(logOnError(errLambda))
.flatMap(doesExist -> existenceHandler(filePart, client, userId));
}
);
}
The methods and lambda expressions named doesContainerExistLambda
, errLambda
, existenceHandler
are subjects of change based on your needs and consideration. The point of the code snippet is to highligh what can be moved elsewhere.
Upvotes: 1