Spring Reactor not working on handle operation while DB fetch - spring-webflux

I'm using spring reactor. The code below is :
public Mono<ResponseEntity<SignUpResponse>> createSignUpForUser(SignUpRequest signUpRequest) {
return Mono.just(signUpRequest)
.map(sign -> {
Mono<UserDetailsEntity> userDetailsEntityMono = userDetailsRepository.findByPhoneNumber(sign.getMobileNumber());
userDetailsEntityMono.handle((user, sink) -> {
if (user != null) {
sink.error(new RuntimeException("Phone number already registered"));
}
});
return functionUserDetails.apply(sign);
})
.flatMap(userDetailsRepository::save)
.map(functionUser)
.flatMap(userRepository::save)
.map(usr -> ResponseEntity.ok(functionSignUpRes.apply(usr)))
.defaultIfEmpty(ResponseEntity.notFound().build())
.log();
}
Here the findByPhoneNumber(sign.getMobileNumber()) DB call is not working (the error is not throwing). The Rest of the operations are working and returning the response. Am i doing anything wrongly ? help me to fix this issue.

I think you need to rewrite your code a bit
public Mono<ResponseEntity<SignUpResponse>> createSignUpForUser(SignUpRequest signUpRequest) {
return userDetailsRepository.findByPhoneNumber(sign.getMobileNumber())
.flatMap(__ -> Mono.error(new RuntimeException("Phone number already registered")))
.switchIfEmpty(userDetailsRepository.save(functionUserDetails.apply(sign)))
.map(functionUser)
.flatMap(userRepository::save)
.map(usr -> ResponseEntity.ok(functionSignUpRes.apply(usr)))
.defaultIfEmpty(ResponseEntity.notFound().build())
.log()
;
}
Idea is that if you find that number then it should error, if result is empty then you would need to insert it so we need to use switch if empty

Related

How to switch flow based on condition?

There is query and two filters. When set search filter and query is not empty, need to one flow. When set checked filter need to other flow.
According debug, onClickSearchFilter or onClickCheckedFilter calls with query or filter changed - return new flow. But in UI no changes, collector dont work second time.
How to switch flow based on condition?
When I debugging with breakpoints in flows, app crash every time A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x14 in tid 23174 (JDWP), pid 23167 . Rebuild, clear cache, reload device - dont' help.
repeatOnStarted(viewModel.itemsFlow) {
// it doesn't work when flow is switched
pagingAdapter.submitData(it)
}
val itemsFlow = queryFlow
.debounce(1000)
.combine(filterFlow) { query, filter ->
when (filter) {
R.id.search_result_chip -> onClickSearchFilter(query)
R.id.checked_chip -> onClickCheckedFilter()
else -> throw Exception("")
}
}.flatMapMerge { // in order to switch from Flow<Flow<*>> to Flow<*>
it
}
private fun onClickSearchFilter(query: String): Flow<PagingData<ItemEntity>> {
return if (query.length < 2)
emptyFlow()
else Pager(BasePagingSource.getConfig()) {
SearchPagingSource(query, client)
}.flow.cachedIn(viewModelScope)
}
private fun onClickCheckedFilter(): Flow<PagingData<ItemEntity>> {
return Pager(
config = BasePagingSource.getConfig(),
remoteMediator = RemoteMediator(checkedIds, cacheDatabase, client)
) {
cacheDatabase.itemDao.getPagingSource(type, checkedIds)
}.flow
}

Mono.zip with null

My code:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId())
)
.flatMap(...
From the frontend we get connect object with 2 fields:
connect{
aCredentialsId : UUID //required
bCredentialsId : UUID //optional
}
So sometimes the second line credentialService.getCredentials(connect.getBCredentialsId())) can return Mono.empty
How to write code to be prepared for this empty Mono when my second field bCredentialsId is null?
What should I do? In case of empty values return Mono.just(new Object) and then check if obj.getValue != null??? I need to fetch data from DB for 2 different values
The strategy I prefer here is to declare an optional() utility method like so:
public class Utils {
public static <T> Mono<Optional<T>> optional(Mono<T> in) {
return in.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty()));
}
}
...which then allows you to transform your second Mono to one that will always return an optional, and thus do something like:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId()).transform(Utils::optional)
).map(e -> new Connect(e.getT1(), e.getT2()))
(...assuming you have a Connect object that takes an Optional as the second parameter of course.)
An easier way is using mono's defaultIfEmpty method.
Mono<String> m1 = credentialService.getCredentials(connect.getACredentialsId());
Mono<String> m2 = credentialService.getCredentials(connect.getBCredentialsId()).defaultIfEmpty("");
Mono.zip(m1, m2).map(t -> connectService.connect(t.getT1(), t.getT2()));
Explanation: if m2 is null then get empty string as a default value instead of null.
Instead of using .zip here, I would work with a nullable property of Connect and use .flatMap in combination with .switchIfEmpty for it.
Kotlin-Version:
val aCredentials = credentialService.getCredentials(connect.getACredentialsId())
credentialService.getCredentials(connect.getBCredentialsId())
.flatMap { bCredentials -> aCredentials
.map { Connect(it, bCredentials)}
.switchIfEmpty(Connect(null, bCredentials))
}
.switchIfEmpty { aCredentials.map { Connect(it, null) } }

Flux.any correct use

I have a Flux.fromIterable(list of ids) . I want to find out if any of the record is null .So I am trying to use Flux.any but I see that I does not even print anything inside any and directly goes to doOnEach as a result output is false. How can we fix this?
The solution should not be limited to null check.It can be any boolean condition.
Mono<Boolean> isAnyNull =Flux.fromIterable(request.getIds())
.switchIfEmpty(Mono.error(new SomeException("No elements")))
.flatMap(id->{
return FooRepo.find(id);
}).any(p->{
System.out.println("check is any null p?."+p);
return ((p==null)||(p.getId()==null));
}).doOnEach(System.out::println);
I tried below as a temp weird fix but i am not sure if this is correct.Also I think it will only work for null.
Mono<Boolean> isAnyNull = Flux.fromIterable(request.getds())
.switchIfEmpty(Mono.error(new SomeException("No elements")))
.flatMap(id -> {
return FooRepo.find(id);
})
.switchIfEmpty(Mono.error(new SomeException("INVALID_ID"))).hasElements()
.doOnEach(System.out::println);
Update : This also works and check null using stream -Comments appreciated to improve
Flux.fromIterable(request.getIds())
.switchIfEmpty(Mono.error(new SomeException("No elements")))
.flatMap(id -> {
return FooRepo.find(id);
})
.any(foo -> {
return (Objects.nonNull(foo)||foo.getId()!=null);
}).doOnEach(System.out::println);

How can I check the total contents of chrome.storage?

In the chrome developer tools you can switch between different contexts in the javascript console and individually query the chrome.storage.local.get() and chrome.storage.sync.get() APIs. Is there a way to view the totality of what is stored there for all of chrome?
If you look at source code of API
bool StorageGetFunction::RunWithStorage(ValueStore* storage) {
Value* input = NULL;
EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
switch (input->GetType()) {
case Value::TYPE_NULL:
return UseReadResult(storage->Get());
case Value::TYPE_STRING: {
std::string as_string;
input->GetAsString(&as_string);
return UseReadResult(storage->Get(as_string));
}
case Value::TYPE_LIST: {
std::vector<std::string> as_string_list;
AddAllStringValues(*static_cast<ListValue*>(input), &as_string_list);
return UseReadResult(storage->Get(as_string_list));
}
case Value::TYPE_DICTIONARY: {
DictionaryValue* as_dict = static_cast<DictionaryValue*>(input);
ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict));
if (result->HasError()) {
return UseReadResult(result.Pass());
}
DictionaryValue* with_default_values = as_dict->DeepCopy();
with_default_values->MergeDictionary(result->settings().get());
return UseReadResult(
ValueStore::MakeReadResult(with_default_values));
}
default:
return UseReadResult(
ValueStore::MakeReadResult(kUnsupportedArgumentType));
}
}
storage type is used as an argument for querying results. So, till date it is not possible to view the totality of what is stored there for all of chrome, you have to use two different queries (chrome.storage.local.get() and chrome.storage.sync.get() APIs).
Have you tried this?
console.log(chrome.storage);

Using boolean fields with Magento ORM

I am working on a backend edit page for my custom entity. I have almost everything working, including saving a bunch of different text fields. I have a problem, though, when trying to set the value of a boolean field.
I have tried:
$landingPage->setEnabled(1);
$landingPage->setEnabled(TRUE);
$landingPage->setEnabled(0);
$landingPage->setEnabled(FALSE);
None seem to persist a change to my database.
How are you supposed to set a boolean field using magento ORM?
edit
Looking at my database, mysql is storing the field as a tinyint(1), so magento may be seeing this as an int not a bool. Still can't get it to set though.
This topic has bring curiosity to me. Although it has been answered, I'd like to share what I've found though I didn't do intense tracing.
It doesn't matter whether the cache is enabled / disabled, the table schema will be cached.
It will be cached during save process.
Mage_Core_Model_Abstract -> save()
Mage_Core_Model_Resource_Db_Abstract -> save(Mage_Core_Model_Abstract $object)
Mage_Core_Model_Resource_Db_Abstract
public function save(Mage_Core_Model_Abstract $object)
{
...
//any conditional will eventually call for:
$this->_prepareDataForSave($object);
...
}
protected function _prepareDataForSave(Mage_Core_Model_Abstract $object)
{
return $this->_prepareDataForTable($object, $this->getMainTable());
}
Mage_Core_Model_Resource_Abstract
protected function _prepareDataForTable(Varien_Object $object, $table)
{
$data = array();
$fields = $this->_getWriteAdapter()->describeTable($table);
foreach (array_keys($fields) as $field) {
if ($object->hasData($field)) {
$fieldValue = $object->getData($field);
if ($fieldValue instanceof Zend_Db_Expr) {
$data[$field] = $fieldValue;
} else {
if (null !== $fieldValue) {
$fieldValue = $this->_prepareTableValueForSave($fieldValue, $fields[$field]['DATA_TYPE']);
$data[$field] = $this->_getWriteAdapter()->prepareColumnValue($fields[$field], $fieldValue);
} else if (!empty($fields[$field]['NULLABLE'])) {
$data[$field] = null;
}
}
}
}
return $data;
}
See the line: $fields = $this->_getWriteAdapter()->describeTable($table);
Varien_Db_Adapter_Pdo_Mysql
public function describeTable($tableName, $schemaName = null)
{
$cacheKey = $this->_getTableName($tableName, $schemaName);
$ddl = $this->loadDdlCache($cacheKey, self::DDL_DESCRIBE);
if ($ddl === false) {
$ddl = parent::describeTable($tableName, $schemaName);
/**
* Remove bug in some MySQL versions, when int-column without default value is described as:
* having default empty string value
*/
$affected = array('tinyint', 'smallint', 'mediumint', 'int', 'bigint');
foreach ($ddl as $key => $columnData) {
if (($columnData['DEFAULT'] === '') && (array_search($columnData['DATA_TYPE'], $affected) !== FALSE)) {
$ddl[$key]['DEFAULT'] = null;
}
}
$this->saveDdlCache($cacheKey, self::DDL_DESCRIBE, $ddl);
}
return $ddl;
}
As we can see:
$ddl = $this->loadDdlCache($cacheKey, self::DDL_DESCRIBE);
will try to load the schema from cache.
If the value is not exists: if ($ddl === false)
it will create one: $this->saveDdlCache($cacheKey, self::DDL_DESCRIBE, $ddl);
So the problem that occurred in this question will be happened if we ever save the model that is going to be altered (add column, etc).
Because it has ever been $model->save(), the schema will be cached.
Later after he add new column and "do saving", it will load the schema from cache (which is not containing the new column) and resulting as: the data for new column is failed to be saved in database
Delete var/cache/* - your DB schema is cached by Magento even though the new column is already added to the MySQL table.