ControlsFX complex validation - validation

I need to validate simple JavaFX form with password and password confirmation.
validationSupport.registerValidator(passwordInput,
Validator.createEmptyValidator("Password required!"));
validationSupport.registerValidator(confirmPasswordInput,
new EqualsToValidator(passwordInput.textProperty(),
"Password differs from confirmation"));
In EqualsToValidator I simply compare property.getValue() and value passed to validator.
If I change only password EqualsToValidator is not called because it is listening to confirmPasswordInput not passwordInput.
I have only found ugly solution:
passwordInput.textProperty().addListener((observable, oldValue, newValue) -> {
String oldText = confirmPasswordInput.getText();
confirmPasswordInput.setText(null);
confirmPasswordInput.setText(oldText);
});
How to invalidate one field when another field changes?

My implementation
Validator.java - base class for validator.
TextValidator.java and PasswordValidator.java - implementations.
ValidationSupport.java tracks all validators.
Usage:
validationSupport.addValidator(new TextValidator(usernameInput, "Username is required!"));
validationSupport.addValidator(new TextValidator(passwordInput, "Password is required!"));
validationSupport.addValidator(new PasswordValidator(passwordInput, confirmPasswordInput, "Password differs from confirmation"));

This worked for me. I simply copied the ValidationSupport class and added the folowing code.
#SuppressWarnings("unchecked")
public <T> void reaplyValidator(Control target){
if(!controls.containsKey(target)){
throw new NullPointerException("The given control was not registered");
}
ValueExtractor.getObservableValueExtractor(target).map( e -> {
ObservableValue<T> observable = (ObservableValue<T>) e.call(target);
Validator<T> validator = (Validator<T>) controls.get(target);
Platform.runLater(() -> validationResults.put(target, validator.apply(target, observable.getValue())));
dataChanged.set(true);
return e;
});
}
public void revalidate(){
for(Control c : getRegisteredControls()){
reaplyValidator(c);
}
}
You also need to change the controls Set to a Map in order to keep the validator.
private ObservableMap<Control, Validator<?>> controls = FXCollections.observableMap(new HashMap<>());

Related

Project Reactor: Obtain Size of List Contained Within a Mono

I'm trying to do something again here in project reactor that I'm sure is reeeeeal simple for any of you project reactor gurus out there!
I've been searching and scratching around with this one for a while now, and feel I'm once again hitting a wall with this stuff.
All I'm trying to do is determine if a List of objects contained within a Mono is empty or not.
This is what I have so far:
private Mono<Boolean> isLastCardForAccount(String accountId) {
return cardService.getAccountCards(accountId)
.hasElement();
}
I'm thinking the above might work, but I'm having difficulty figuring out how to extract/access the 'Boolean' contained within the returned Mono. I think I have to use 'subscribe' somehow right?
I've mucked around with this stuff for a while now, but still no luck.
Here is how 'getAccountCards' is defined:
public Mono<List<Card>> getAccountCards(final String accountId) {
return cardCrudRepository.getCardsByAccountId(accountId)
.collectList();
}
From CardCrudRepository:
// #Query("SELECT * FROM card WHERE account_id = :accountId") <-Not sure if I need this
Flux<Card> getCardsByAccountId(String accountId);
And lastly, how I'm using 'isLastCardForAccount':
public Mono<Void> updateCardStatus(String accountId, String cardId, String cardStatus) {
return accountService.getAccount(accountId)
.map(Account::getClientId)
.map(clientId -> createUpdateCardStatusServiceRequestData(clientId, cardId, cardStatus))
.flatMap(requestData -> cartaClient.updateCardStatus(requestData)
.then(Mono.defer(() -> isCardBeingCancelled(cardStatus) ? allCardsCancelledForAccount(accountId) ? removeAccount(accountId) :
(isLostOrStolen(cardStatus) ? replaceCard(cardId, cardStatus).flatMap(this::updateCardNumber) : Mono.empty()) : Mono.empty())));
}
As always, any and all help and insight is tremendously appreciated!
I am not sure if this would resolve the issue but this way you can try to write your logic
return accountService.getAccount(accountId)
.map(Account::getClientId)
.map(clientId -> createUpdateCardStatusServiceRequestData(clientId, cardId, cardStatus))
.flatMap(requestData -> cartaClient.updateCardStatus(requestData)
.then(Mono.defer(() ->
Mono.zip(
Mono.just(isCardBeingCancelled(cardStatus)),
isLastCardForAccount(accountId),
Mono.just( isLostOrStolen(cardStatus) )
)
.map(tuple -> {
WRITE YOUR IF ELSE LOGIC
})
The idea is to use zip and then use the tuple for writing logic. The Tuple would be of type Tuple3 of <Boolean, Boolean ,Boolean>. I made the assumption that isLostOrStolen(cardStatus) returns Boolean.
One way of doing that is by using filterWhen operator like this:
.then(Mono.defer(() -> {
if (isCardBeingCancelled(cardStatus)) {
return Mono.just(accountId)
.filterWhen(this::allCardsCancelledForAccount)
.flatMap(this::removeAccount);
} else if (isLostOrStolen(cardStatus)) {
return replaceCard(cardId, cardStatus).flatMap(this::updateCardNumber);
}
return Mono.empty();
}))
You can use filterWhen in the case of asynchronous filtering. Check this section of Which operator do I need? reference and this How to filter Flux asynchronously.
As a side note, this is not going to work as you expect:
private Mono<Boolean> isLastCardForAccount(String accountId) {
return cardService.getAccountCards(accountId)
.hasElement();
}
public Mono<List<Card>> getAccountCards(final String accountId) {
return cardCrudRepository.getCardsByAccountId(accountId)
.collectList();
}
The collectList() will emit an empty List if there is no card. I'd use exists query instead:
public Mono<Boolean> isLastCardForAccount(final String accountId) {
return cardCrudRepository.existsByAccountId(accountId);
}

How can I use Optional to return empty if there is any exception in the code in it?

Pretty new to Optional/Java8 usage and I had a doubt regarding its usage.
I have a function in my API which returns a String which can be null or empty. Its similar to finding the id in email like: abc#gmail.com -> abc is o/p.
Now one way of doing this was:
public Optional<String> getUser(final String emailId) {
if (TextUtils.isEmpty(emailId)) {
return Optional.empty();
}
String userIDSeparator = "#";
int userNameEndIndex = emailId.indexOf(userIDSeparator);
if (userNameEndIndex == -1) {
return Optional.empty();
}
return Optional.of(emailId.substring(0, userNameEndIndex));
}
I was wondering if there is any neater way of doing this to return an Optional?
Also, Optional was introduced in Java8 so is there anyway the code can be java7 compatible? I am aware of preprocessors in C not sure if something similar is available.
Note:
I have not compiled this code, so there might be some errors. I wanted the input on Optional.
Thanks!
Well, the code can certainly be reduced. i.e.
public Optional<String> getUser(final String emailId) {
return Optional.of(emailId)
.filter(email -> email.contains("#"))
.map(email -> email.substring(0, email.indexOf("#")));
}
if this method can ever receive a null value then you'd need to change Optional.of(emailId) to Optional.ofNullable(emailId).
As for:
Also, Optional was introduced in Java8 so is there any way the code can
be java7 compatible?
Not that I know of. There may be other libraries that have similar functionality to Java's Optional type, so a little bit of research online may get you to the right place.
Maybe you mean something like this :
public Optional<String> getUser(final String emailId) {
return Optional.ofNullable(emailId)
.filter(email -> email.contains("#"))
.map(email -> Optional.of(email.replaceAll("(.*?)#.*", "$1")))
.orElseGet(Optional::empty);
}
Example
null -> Optional.empty
"" -> Optional.empty
"abc#gmail.com" -> abd
As #Aominè mention, there are some unnecessary parts in my solution, you can use this version instead :
public Optional<String> getUser(final String emailId) {
return Optional.ofNullable(emailId)
.filter(email -> email.contains("#"))
.map(email -> email.replaceAll("(.*?)#.*", "$1"));
}

Using FormFlow Bots Framework Quiz Program

Our bot build does a ‘personality quiz’ for the user. Think Buzzfeed.
I have a variety of attributes I want to increase, just integers, based on the user’s selections on a form, then return an end result.
Using Sandwichbot as a template, this is asking something like (paraphrased):
Do you like to help other people? Yes No
Code is like:
.Confirm(async (state) =>
{
switch (state.HelpYesNo)
{
case true: HelpfulValue++; break;
case false: HurtfulValue++; break;
}
return new PromptAttribute("Thanks, choose OK to continue.");
It works fine, but I hate that I have to make the user ‘Confirm’ by typing OK. It’s an extra step, especially if they have to do it after each question.
I tried writing this with a validate instead, eg validate: async (state, response) =>
Which gives a better user experience, but doesn’t actually run the switch-case. I think the formatting of the switch is in the wrong place for a validate? I'm not sure of the syntax here to get 'validate' to process the case.
What’s the right way to do something like this in FormFlow?
Try something like this. Boolean fields also result in a Yes/No question.
[Serializable]
public class QuizForm
{
public int HelpfulValue;
public int HurtfulValue;
[Prompt("Do you like to help people? {||}")]
public bool HelpPeople;
public static IForm<QuizForm> BuildForm()
{
return new FormBuilder<QuizForm>()
.Message("Let the quiz begin...")
.Field(nameof(HelpPeople), validate: ValidateBool)
// other fields
.Build();
}
private static async Task<ValidateResult> ValidateBool(QuizForm state, object value)
{
var TrueOrFalse = (bool) value;
switch (TrueOrFalse)
{
case true: state.HelpfulValue++; break;
case false: state.HurtfulValue++; break;
}
return new ValidateResult{IsValid = true, Value = value};
}
}

Grails tries to validate encoded password (Spring Security Core)

I spent hours on this problem without finding the solution. Other questions come close but none of the suggested solutions work for me.
I'm running on
- Grails 2.1.1 installed with
- Groovy 2.0.8 and
- Oracle Java v 1.6.0_45 (also tried with 1.7 already)
I added the Spring Security Core Plugin v 2.0-RC2.
I'm a Grails beginner and all I want to do is create a "Runner" with a password using my own password validator.
This is my Runner.groovy domain class (did not change very much from the default Spring Security User template apart from the renaming):
package de.muden.runnerbase
class Runner {
transient springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
Date dateCreated
Profile profile
public static final int MIN_PASS_LENGTH = 6
public static final int MAX_PASS_LENGTH = 20
static transients = ['springSecurityService']
static constraints = {
username(size:3..20,unique:true)
password(nullable:false, blank:false, minSize:6, validator: { passwd, runner ->
return (passwd != runner.username && validatePasswordInplace(passwd))
})
dateCreated()
profile(nullable:true)
}
static mapping = {
profile lazy:false
runs sort:'dateCreated'
password column: '`password`'
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
String toString() {
return "Runner '$username'"
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
protected static boolean validatePasswordInplace(String passToValidate) {
println "VALIDATING PASS $passToValidate"
return passToValidate ==~ /([A-Za-z0-9äöüÄÖÜß.!\?_-]){$MIN_PASS_LENGTH,$MAX_PASS_LENGTH}/
}
static hasMany = [runs: Run, shoes: Shoe]
}
So the validator allows passwords between 6 and 20 characters long, upper and lower case letters, numbers and a few special characters.
Simple unit tests just testing this method work as expected.
Now a simple integration test:
void testValidRunner() {
Runner r = new Runner(username:'dummy',password:'foobar')
assertTrue r.validate() // OK
assertFalse r.hasErrors() // OK
assertNotNull r.save(flush:true,failOnError:true) // OK
Runner foundRunner = Runner.findByUsername("dummy")
assertNotNull foundRunner // fails, foundRunner = null
assertEquals('dummy',foundRunner.username)
}
And the console (with -echoOut) says:
VALIDATING PASS foobar
VALIDATING PASS $2a$10$Q5RYaDrCFFxdXEqYqV4J2OJWHzgOJZJ3wljqVK1jNP4Sqm6ZUOPam
It is obvious that the second validation fails. But why is grails validating the encoded password again? And why doesn't r.validate() complain? Where exactly does that second validation happen?
I have the feeling that I'm doing really basic user password encryption wrong here...
First I thought it had to do with the Spring Security fields "accountExpired" etc. being added and not in the constraints block. But when I remove the custom validator everything works fine.
Any help is appreciated :-)
Thanks,
Matt
Below is what I think is going on ...
The second validation happens when you call r.save
The beforeInsert method is calling the encodePassword method, which is encoding the PW to the long string $2a$10$Q5RYaDrCFFxdXEqYqV4J2OJWHzgOJZJ3wljqVK1jNP4Sqm6ZUOPam
That is the string that will be validated and saved in the DB not 'foobar'
I believe that your regex does not allow the dollar sign, which is part of the encoded string resulting in the failure.
I do not think that using constraints will work for what you want to do. You need to validate before it is encoded, so you probably need to add separate validation code (in the domain class or elsewhere) and validate the PW before assigning to the PW field and saving the object.

Web API validation error

I have a View Model called SignUp with the EmailAddress property set like this:
[Required]
[DuplicateEmailAddressAttribute(ErrorMessage = "This email address already exists")]
public string EmailAddress { get; set; }
and the custom validator looks like this:
public class DuplicateEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
PestControlContext _db = new PestControlContext();
int hash = value.ToString().GetHashCode();
if (value == null)
{
return true;
}
if (_db.Users.Where(x => x.EmailAddressHash == hash).Count() > 0)
return false;
else
return true;
}
}
The problem I'm having is that if the user leaves the email address field blank on the sign up form the application is throwing a null reference exception error (I think it's looking for "" in the database and can't find it). What I don't understand is why this isn't being handled by the Required attribute - why is it jumping straight into the custom validator?
The Required attribute would have resulted in an error being added to the model state. It will not short-circuit the execution though. The framework continues to run other validators for the simple reason that all the errors about the request need to be sent out in a single shot. Ideally, you wouldn't want the service to say something is wrong to start with and when the user re-submits the request after making a correction, the service comes back and say some other thing is wrong and so on. It will be an annoyance, I guess.
The NullReferenceException is thrown because value.ToString() is called before the check against null. As you need the hash variable only after the check, you can solve this by reordering the statements:
if (value == null)
{
return true;
}
int hash = value.ToString().GetHashCode();
In addition, you could also move the PestControlContext after the check against null and use a using statement to dispose of it properly.
As also #Baldri pointed out, each validator can add Error messages and all of them are run, even if a previous one already signaled the data to be invalid. Furthermore, I'd not rely on that the validations are run in the order that you specify when marking the property with the attributes (some frameworks implement their own attribute ordering mechanism in order to assert that the order is deterministic, e.g. priorities or preceding attributes).
Therefore, I suggest reordering the code in the custom validator is the best solution.

Resources