Spring Data Rest - Custom LinkRelationProvider is never used - spring-data-rest

I am creating a service based on spring-boot-starter-parent 2.6.1 and use spring data rest to expose my JPA repository:
public interface PointRepo extends CrudRepository<Point<?>, String> {}
The Point type has subtypes Quantity and Switch, so a GET /points currently returns something like:
{
"_embedded" : {
"switches" : [ {
...
"_links" : {
"self" : {
"href" : "http://localhost:8080/switch/office_light"
},
...
}
}],
"quantities" : [ {
...
"_links" : {
"self" : {
"href" : "http://localhost:8080/quantity/office_temp"
},
...
}
}
Since I am not planning to expose endpoints /switches and /quantities, I want all Points to be in _embedded.points and the self hrefs to point to /points, too. I figured, I need a custom LinkRelationProvider so I created this:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class PointRelProvider extends EvoInflectorLinkRelationProvider {
#Override
public LinkRelation getCollectionResourceRelFor(final Class<?> type) {
return super.getCollectionResourceRelFor(Point.class);
}
#Override
public LinkRelation getItemResourceRelFor(Class<?> type) {
return super.getItemResourceRelFor(Point.class);
}
#Override
public boolean supports(LookupContext delimiter) {
return Point.class.isAssignableFrom(delimiter.getType());
}
}
I found out the bean gets created, but it has no effect on the output whatsoever. I put breakpoints into each method put none of them ever gets called. Any ideas why that might be the case?

Related

More than one IViewComponentResult method in ViewComponent Class

Is it possible to access more than one IViewComponentResult method in a single ViewComponent Class? I seem to be able to create more then one method with each referencing their own View, but I have been unable to find a way to reference the individual methods within the class from a razor view.
public class PageHeaderViewComponent: ViewComponent
{
public IViewComponentResult Header(LayoutModel layoutModel)
{
return View("Header", layoutModel);
}
public IViewComponentResult PageHeaderUpper(LayoutModel layoutModel)
{
return View("PageHeaderUpper", layoutModel);
}
}
I suggest you can pass a parameter to Invoke method to conditionally return different view:
public class PageHeaderViewComponent : ViewComponent
{
public IViewComponentResult Invoke(LayoutModel layoutModel,string name)
{
if(name=="header")
{
return View("Header", layoutModel);
}
else
{
return View("PageHeaderUpper", layoutModel);
}
}
}
You can invoke the view components like below in main view:
#await Component.InvokeAsync(typeof(PageHeaderViewComponent), new { LayoutModel= new LayoutModel() , name = "header" })
#await Component.InvokeAsync(typeof(PageHeaderViewComponent), new { LayoutModel= new LayoutModel() , name = "aaa" })
View Components location:
Besides, you can also use the property in LayoutModel as the condition.

How can I validate different types within a collection using FluentValidation?

I have a class with a collection that needs validation. The generic on the collection takes an interface and different types can be added to the collection.
What is the cleanest path forward to creating a FluentValidation validator that supports polymorphism?
public interface IWizardStep {}
public class WizardOne : IWizardStep
{
public string Model { get; set; }
}
public class WizardTwo : IWizardStep
{
public string FirstName { get; set; }
}
public class Wizard
{
public Wizard()
{
var w1 = new WizardOne();
var w2 = new WizardTwo();
Steps = new List<IWizardStep>
{
w1,
w2
};
}
public IList<IWizardStep> Steps { get; set; }
}
public class WizardValidator : AbstractValidator<Wizard>
{
public WizardValidator()
{
RuleFor(x => x.Steps)
// Steps First where is WizardOne
// Model.NotEmpty()
// Steps First where is WizardTwo
// FirstName.NotEmpty()
}
FluentValidation doesn't support polymorphism for child collections like this out of the box, but you can add this behaviour by using a custom property validator, or by using OfType in your rule definitions.
I've written about both approaches before here:
Step 1: Create a validator for each implementor
Start by creating a validator for WizardOne and WizardTwo:
public class WizardOneValidator : AbstractValidator<WizardOne> {
public WizardOneValidator() {
RuleFor(x => x.Model).NotEmpty();
}
}
public class WizardTwoValidator : AbstractValidator<WizardTwo> {
public WizardTwoValidator() {
RuleFor(x => x.FirstName).NotEmpty();
}
}
Step 2: Create the parent validator
You have two options for defining the parent validator. The simplest approach is to use OfType, but this is less performant. The more complex option is to use a custom property validator.
Option 1: Using OfType
public WizardValidator : AbstractValidator<Wizard> {
public WizardValidator() {
RuleForEach(x => x.Steps.OfType<WizardOne>()).SetValidator(new WizardOneValidator());
RuleForEach(x => x.Steps.OfType<WizardTwo>()).SetValidator(new WizardTwoValidator());
}
}
This is the simplest approach, but calling OfType inside the call RuleFor will end up bypassing FluentValidation's expression cache, which is a potential performance hit. It also iterates the collection multiple. This may or may not be an issue for you - you'll need to decide if this has any real-world impact on your application.
Option 2: Using a custom PropertyValidator.
This uses a custom custom validator which can differentiate the underlying type at runtime:
public WizardValidator : AbstractValidator<Wizard> {
public WizardValidator() {
RuleForEach(x => x.Steps).SetValidator(new PolymorphicValidator<Wizard, IWizardStep>()
.Add<WizardOne>(new WizardOneValidator())
.Add<WizardTwo>(new WizardTwoValidator())
);
}
}
Syntactically, this isn't quite as nice, but doesn't bypass the expression cache and doesn't iterate the collection multiple times. This is the code for the PolymorphicValidator:
public class PolymorphicValidator<T, TInterface> : ChildValidatorAdaptor<T, TInterface> {
readonly Dictionary<Type, IValidator> _derivedValidators = new Dictionary<Type, IValidator>();
// Need the base constructor call, even though we're just passing null.
public PolymorphicValidator() : base((IValidator<TInterface>)null, typeof(IValidator<TInterface>)) {
}
public PolymorphicValidator<T, TInterface> Add<TDerived>(IValidator<TDerived> derivedValidator) where TDerived : TInterface {
_derivedValidators[typeof(TDerived)] = derivedValidator;
return this;
}
public override IValidator<TInterface> GetValidator(PropertyValidatorContext context) {
// bail out if the current item is null
if (context.PropertyValue == null) return null;
if (_derivedValidators.TryGetValue(context.PropertyValue.GetType(), out var derivedValidator)) {
return new ValidatorWrapper(derivedValidator);
}
return null;
}
private class ValidatorWrapper : AbstractValidator<TInterface> {
private IValidator _innerValidator;
public ValidatorWrapper(IValidator innerValidator) {
_innerValidator = innerValidator;
}
public override ValidationResult Validate(ValidationContext<TInterface> context) {
return _innerValidator.Validate(context);
}
public override Task<ValidationResult> ValidateAsync(ValidationContext<TInterface> context, CancellationToken cancellation = new CancellationToken()) {
return _innerValidator.ValidateAsync(context, cancellation);
}
public override IValidatorDescriptor CreateDescriptor() {
return _innerValidator.CreateDescriptor();
}
}
}
This will probably be implemented in the library as a first class feature at some point in the future - you can track its development here if you're interested.

UnrecognizedPropertyException: Unrecognized field - jackson 2.9

I am doing a simple conversion using Jackson:
response = mapper.readValue(responseStr, PrinterStatus.class);
The code is throwing this exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "OutputParameters" (class com.xerox.PrinterStatus),
not marked as ignorable (one known property: "outputParameters"]) at ....
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:823)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1153)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1589)
The Json I would like to convert is very simple:
{
"OutputParameters": {
"#xmlns": "http://xmlns.xerox.com/apps/rest/",
"#xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"GETPRINTERSTATUS": {
"GETPRINTERSTATUS_ITEM": [{
"STATUS": "True",
"MESSAGE": " "
}]
}
}
}
This is the PrinterStatus class, it has the field "OutputParameters"
So I am not sure what is Jackson yelling about.
public class PrinterStatus {
private OutputParameters outputParameters;
public OutputParameters getOutputParameters() {
return outputParameters;
}
public void setOutputParameters(OutputParameters outputParameters) {
this.outputParameters = outputParameters;
}
...
Basically JSON keys are case sensitive. Accordingly OutputParameters doesn't equal to outputParameters.
So you have to choose:
rename the field in Java class (and getters / setters too) to OutputParameters
rename JSON property key to outputParameters
If you using Jackson 2.9 or above just simply annotate field like this:
public class PrinterStatus {
#JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
private OutputParameters outputParameters;
public OutputParameters getOutputParameters() {
return outputParameters;
}
public void setOutputParameters(OutputParameters outputParameters) {
this.outputParameters = outputParameters;
}
...
}
Set property name explicitly
public class PrinterStatus {
#JsonProperty("OutputParameters")
private OutputParameters outputParameters;
...
}
Enable case insesitive feature globally
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);

Unwrap only some properties with Jackson

Assuming I have this objects:
class Person {
String name;
Household getHousehold();
}
class Household {
Set<Address> getAddresses();
String householdId;
}
which would normally be serialized as follows
{
"name": "XXX",
"household": {
"addresses": [...]
}
}
Is there a way to configure Jackson with annotations / mix-ins to obtain this (ie. without using DTO) ?
{
"name": "XXX",
"addresses": [...],
"household": {
"householdId": 123
}
}
You can configure the unwrapping of a specific property by both using mixins and annotations:
1. Mixins
Assuming you define the following mixin:
public abstract class UnwrappedAddresses {
#JsonUnwrapped
public abstract Household getHouseHold();
}
And then add a custom module to your objectMapper which applies the mixin to the Person class as follows:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper .registerModule(new SimpleModule() {
#Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Person.class, UnwrappedAddresses.class);
}
});
This approach does not change the Household serialization as a single item, but just unwraps a household item when it's encapsulated in a Person object.
2. Annotations
Just add #JsonUnwrapped to your getHouseHold() method.
EDIT: After post changes.
What you want is basically to change the output of the json, which can be done by using the #JsonAnyGetter annotation(which can dynamically add new properties to your pojo).
Your expected result can be achieved by ignoring the household property and unwrapping it with the help of the #JsonAnyGetter.
#JsonIgnoreProperties("houseHold")
public static class Person {
String name;
Household houseHold;
#JsonAnyGetter
public Map<String,Object> properties(){
Map<String,Object> additionalProps=new HashMap<>();
additionalProps.put("addresses", new ArrayList<>(houseHold.getAddresses()));
Map<String,Object> houseHolProps=new HashMap<>();
houseHolProps.put("houseHoldId", houseHold.id);
additionalProps.put("houseHold", houseHolProps);
return additionalProps;
}
..getters&setters omitted
}
Which would after serialization return
{"name":"name",
"houseHold":{"houseHoldId":0},
"addresses":[
{"houseNo":2,"street":"abc"},
{"houseNo":1,"street":"str"}
]
}

NHibernate Component inheritance

Is it possible to do inheritance with NHibernates component? A quick google returned only a hand full of appropriate results (all blogs) and all were written some time ago so was wondering if it is available yet?
if not how do you handle it instead?
It's not currently possible. If you need inheritance, you have to map an entity.
In addition to what Diego had said, there is also an option to implement custom hydration/dehydration for hierarchy using IUserType. See this article for details (its Java but the same mechanism can be used in C#).
And Hibernate feature request is here. NHibernate version is here, please vote.
Yes.
public class A { }
public class B : A { }
public sealed class C : B { }
public abstract class BaseAMap<T> : ComponentMap<T> where T : A {
public BaseAMap() {
// Map A here
}
}
public class AMap : BaseAMap<A> {
}
public class BaseBMap<T> : BaseAMap<T> where T : B {
public BaseBMap () {
// Map B (excluding A)
}
}
public class BMap : BaseBMap<B> {
}
public sealed class CMap : BaseBMap<B> {
public CMap () {
// Map C (excluding B)
}
}