In a Quarkus (version 2.9.2.Final) application (not using Spring), with hibernate validation on a REST API, the objective is to validate the input parameters of a method. Everything works fine, except changing the class name to the one used in the api.
How can it be changed the class name on violations.field value of the exception to use the one used on the Rest API (defined in JsonTypeName?
The parameter class name:
#JsonTypeName("some_class")
public record SomeClass(
#NotNull #JsonProperty("timestamp_api") LocalDateTime timestamp) {
}
Resource class:
#Path("some_path")
public class SomeResource {
#POST
public Response send(#Valid SomeClass somePath) {
System.out.println("RECEIVED 2: " + somePath);
return Response.accepted().build();
}
}
Already implemented interface PropertyNodeNameProvider as indicated in Hibernate Validator documentation, but it only changes parameters of and not the class itself.
Calling the service with:
{}
Gives an error like:
{
"title": "Constraint Violation",
"status": 400,
"violations": [
{
"field": "send.someClass.timestamp_api",
"message": "must not be empty"
}
] }
The objective is to change someClass (internal name) to some_class (api name).
Related
I'm trying to use Spring Data Rest to implement a full set of services for about 60 entities. Right now, I'm getting by with just letting Spring use my repositories rather than implementing controllers, which is great!
The data I'm having to model isn't ideal--I'd prefer to have customerId come as part of the order object.
{
"tenantId": 42,
"id": "00000001",
"customer": {
"tenantId": 42,
"id": "CUST001",
"name": "Arthur Dent"
}
}
I have the ID for a related entity as a property on my JSON object.
public class Order {
Long tenantId;
String id;
String customerId;
}
I don't really want to pull the full Customer entity and all of the other related entities and place them as members on my Order object. Instead, I'd just like to add some links to the _links collection.
I believe I've figured out WebMvcLinkBuilder finally and I have the basic idea in place. However, JpaRepository.findById returns a java.util.Optional.
#Bean
public RepresentationModelProcessor<EntityModel<Order>> orderProcessor() {
return new RepresentationModelProcessor<EntityModel<Order>>() {
#Override
public EntityModel<Order> process(final EntityModel<Order> model) {
final CustomerRepository controller = WebMvcLinkBuilder.methodOn(CustomerRepository);
final CustomerId id = new CustomerId(model.getContent().getTenantId(), model.getContent().getCustomerId());
model.add(WebMvcLinkBuilder.linkTo(controller.findById(id)).withRel("customer"));
return model;
}
};
}
The error I get is:
Could not generate CGLIB subclass of class java.util.Optional: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class java.util.Optional
How can I add a link to my resource?
I want to use ExtendedScalars.Json from the graphql-java-extended-scalars package in my application which utilizes the quarkus-smallrye-graphql however I am struggling doing so.
Given a Test model class like
public class Test {
public String name;
public Object description;
}
with following GraphQLApi
#GraphQLApi
public class API {
#Inject
SomeService someService;
#Query
public Test getTest() {
return someService.getTest();
}
}
where Test might be described by
{
"name": "Test",
"description": {
"hello": "world"
}
}
the field description should be treated as ExtendedScalars.Json, so the result of this GraphQL query
{
test {
description
}
}
should be exactly
{
"data": {
"test": {
"description": {
"hello": "world"
}
}
}
}
However the #ToScalar annotation does not take ExtendedScalars.Json and falling back to graphql-java with something like public GraphQLSchema.Builder addScalar(#Observes GraphQLSchema.Builder builder) { ... } to manually replace the Type and QueryType leads to a AssertException like
graphql.AssertException: All types within a GraphQL schema must have unique names. No two provided types may have the same name.
No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'Query' from being a 'GraphQLObjectType' to a 'GraphQLObjectType'
Any clue how to handle this?
I have a simple json like this
{
"someReports":[
{
"reportName": "PR123",
"fields": [
]
},
{
"reportName": "PR234",
"fields": []
}
]
}
I have a class that looks somewhat like this inside which getSomeReports() is defined.
class AHeckLotOfReports {
private String someString;
private List<SomethingElse> some;
..
#JsonProperty("someReports")
private List<SomeReports> someReports;
}
//POJO:
class SomeReport {
String reportName;
List<Field> fields;
...
}
//REST Controller looks like this. some injection code is cleaned up
#Api(tags = “SomeReport”)
#Controller
#ThreadSafe
#RequestMapping(“/report/v2")
public class ReportController{
#ApiOperation(value = "Create a new report.”)
#RequestMapping(value = “/report”, method = RequestMethod.POST)
#ResponseBody
public ReportResponse addReport(
#Nonnull final AuthorizationToken authorizationToken,
#Nonnull#RequestBody final AHeckLotOfReports reportRequest) {
final Report report=this.reportService.addReport(
authorizationToken,
reportRequest.getName(),
reportRequest.isEnabled(),
reportRequest.getConfiguration().or(ReportConfiguration.empty()),
reportRequest.getNewConfiguration(),
reportRequest.getDefinition());
return ReportResponse.fromReport(report);
}
I haven't been able to get this working. I do get the structure intact but reportName comes up blank, the fields array comes up empty.
I have tried JsonAlias("someReports","some_reports") and that seems to make no difference.
Anything comes out as an obvious "duh" in this ?
EDIT: My apologies, I did realize I have not provided the entire context. I AM able to deserialize the simple POJO with ObjectMapper. But the class AHeckLotOfReports is used as request object in REST endpoint for a POST and this is where the problem surfaces
Software: jackson.core 2.9.9, jackson.datatype 2.9.8 and JDK 8.0, Spring-Boot 2.2.6
I have one JSON which looks something like this
{
"uniqueId":"junk",
"buildingId":"123",
"famousFor":[
{
"famousForId":"asd",
"name":"Gaming",
"activeState":true
}
],
"openHours":[
{
"day":"Sunday",
"timingsFrom":{
"time":"11:00",
"meridian":"AM"
},
"timingsTo":{
"time":"11:59",
"meridian":"PM"
}
}
],
"uploadedImages":{
"coverPhoto":[
{
"imageUrl":"http://google.com/images/a123a.png",
"activeState":false
}
],
"profilePhoto":[
{
"imageUrl":"http://google.com/images/a123a.png,
" activeState":false
}
]
},
"fDescriptions":[
{
"fMapUrl":"http://google.com/images/a123a.png",
"tag":"1"
}
],
"Outlets":[
{
"outletName":"Halo",
"floorNumber":1,
"category":"Gaming"
}
]
}
Now the thing is I have to create one GET API which essentially will provide me the same template with empty value. While returning the Object it's sending me the null value. How can I standardized the template that looks the same. My Object looks something like this.
public class EssentialDetails {
#NotBlank(message=ApplicationUtil.MISSING_FIELD)
#Valid
#Pattern(regexp = "[0-9]+$",message="DP ID Must be Number")
String dpId;
#Id #NotBlank(message= ApplicationUtil.MISSING_FIELD)
#Valid
#Pattern(regexp = "[A-Za-z0-9]+$",message="Must Be Combination of Number and Letters")
String tpId;
#NotNull(message=ApplicationUtil.MISSING_FIELD) #Valid
List<FamousFor> famousFor;
#NotNull(message=ApplicationUtil.MISSING_FIELD) #Valid
List<OpenHours> openHours;
#NotNull(message=ApplicationUtil.MISSING_FIELD) #Valid
Pictures uploadedImages;
#NotNull(message=ApplicationUtil.MISSING_FIELD) #Valid
List<FloorDescription> floorDescriptions;
#NotNull(message=ApplicationUtil.MISSING_FIELD) #Valid
List<Outlets> mallOutlets;
}
How can I pass the empty template with every field present within the template? I'm using java 8 and spring boot 2.0.6.
Incase of empty or null or some valid values below annotation can be used above property of your model
#JsonInclude(ALWAYS)
This is the Code
Person.java
#Entity
class Person {
#Id private Long Id;
#NotNull private String name;
//getter setters
}
PersonRepository.java
#RepositoryRestResource(collectionResourceRel = "person", path="person" )
interface PersonRepository extends CrudRepository<Person,Long>{
}
Now, when I send null against name attribute , the validator validates it correctly, but the actual exception that is getting thrown is TransactionRollbackExecption.
Like this
{
"timestamp": "2018-03-14T09:01:08.533+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction",
"path": "/peron"
}
How do I get the actual ConstraintViolation exception. I do see the exception in logs. But its not getting thrown.
You can add LocalValidatorFactoryBean to ValidatingRepositoryEventListener when configuring RepositoryRestConfigurerAdapter, like this:
#Configuration
public class RepoRestConfig extends RepositoryRestConfigurerAdapter {
private final LocalValidatorFactoryBean beanValidator;
public RepoRestConfig(LocalValidatorFactoryBean beanValidator) {
this.beanValidator = beanValidator;
}
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", beanValidator);
v.addValidator("beforeSave", beanValidator);
super.configureValidatingRepositoryEventListener(v);
}
}
The reason for this is that Spring's TransactionInterceptor is overriding your exception.
The idiomatic way of implementing repository entity validation, according to Spring's documentation, is to use Spring Data Rest Events. You probably want to use BeforeSaveEvent or BeforeCreateEvent.
You can create a custom type-safe handler for entities (see the provided link for details), which looks similar to:
#RepositoryEventHandler
public class PersonEventHandler {
#HandleBeforeSave
public void handlePersonSave(Person p) {
// … you can now deal with Person in a type-safe way
}
}
Another approach is to register a Repository Listener which extends AbstractRepositoryEventListener, also described in the documentation.