Closed nealeduncan1 closed 2 years ago
Hi @nealeduncan1
Right now there are two ways to accomplish that. You can: 1) Create an abstract command handler that will invoke the validator. 2) Create a middleware that will resolve and invoke the validator.
Would that work for you?
Hi @sizovs - Option 2 was something I was looking at. Do you have an example of how this might work? I wasn't sure how to 'find' the correct instance of the CommandValidator to invoke it.
@nealeduncan1
1) If you're using Spring 5+, here is the pseudo-code for the Middleware:
@Component
class ValidationMiddleware implements Command.Middleware {
ValidationMiddleware(ObjectProvider<CommandValidator> validators){
this.validators = validators;
}
@Override
public <R, C extends Command<R>> R invoke(C command, Next<R> next) {
CommandValidator validator = validators.stream().filter(validator -> validator.matches(command)).findFirst();
validator.validate(command);
return next.invoke();
}
}
2) To "match" validator with the command, you can rely on generics and Guava's TypeToken:
interface CommandValidator<C extends Command<R>, R> {
default boolean matches(C command) {
TypeToken<C> typeToken = new TypeToken<C>(getClass()) {
};
return typeToken.isSupertypeOf(command.getClass());
}
}
3) Finally, add the middleware to PipelinR
Got it working now - thank you! Might be worth adding something similar as an example as I think it's a fairly common use case for validation?
Good idea. Documented: https://github.com/sizovs/PipelinR/commit/c0746d25940ce6664f3de9cac3e4af1874c43fcd.
Closing the issue.
Hi - actually, this isn't working quite right. The matches(...) method is finding the wrong CommandValidator. It appears to 'find' the first CommandValidator, so I don't think the logic in the matches is working as expected? The typeToken is java.lang.Object so I'm not sure if this is an issue?
Got it sorted now. I had changed my interface to use local type inference, i.e.:
interface CommandValidator<C extends Command<R>, R> {
default boolean matches(C command) {
var typeToken = new TypeToken<>(getClass()) {
};
return typeToken.isSupertypeOf(command.getClass());
}
}
But making it explicit (TypeToken<C>
) fixed my issue.
I am looking for a way to invoke a pipeline behaviour so that it will automatically perform a common action such as validation, before the main handler is called. Right now I need to manually call new CommandValidator(command) as the first line in my handle(...) method.
For example ( I'm using your Validator helper class. ):
Would it be possible to add the ability to have this type of behaviour as part of the pipeline?