I have two spring based applications.
Application 1 is the returning list based on sortOrder
Example: [{"name": "Abc", "sortOrder": 1}, {"name": "Xyz", "sortOrder": 2}]
This order is maintained while fetching, I validated through Postman rest client.
Application 2, We have restTemplate, which fetches this list from Application 1. The response type we put is List, But I am not getting a list in order manner.
I looked into convertors. But no luck.
Related
I'm going to define Open Api schema for an endpoint implemented in JAVA, which returns JSON like this:
{"id": 1, "data": [[int, int]]}
Any idea how to configue the annotation #Schema?
I am trying to write deserialization code for responses of user-defined GraphQL queries. The code has access to the query response in JSON-serialized form and the underlying GraphQL schema (by querying the endpoint's schema.json or making introspection requests).
Assume the following schema:
scalar Date
type User {
name: String
birthday: Date
}
type Query {
allUsers: [User]
}
schema {
query: Query
}
And the following query:
query {
allUsers {
name
birthday
}
}
The response may look like this (only includes the data.allUsers-field from the full response for brevity):
[
{"name": "John Doe", "birthday": "1983-12-07"}
]
What I am attempting to do is deserialize the above response in a manner that preserves type information, including for any custom scalars. In the above example, I know by convention that the GraphQL scalar Date should be deserialized as LocalDate in Java, but just from the response alone I do not know that the birthday field represents the GraphQL scalar type Date, since it's serialized as a regular string in JSON.
What I can do is try to utilize the GraphQL schema for this. For the above example, the schema may look something like this (shortened for brevity):
...
"types": [
{
"kind": "OBJECT",
"name": "User",
"fields": [
{
"name": "name",
"type": {
"kind": "SCALAR",
"name": "String"
}
},
{
"name": "birthday"
"type": {
"kind": "SCALAR",
"name": "Date"
}
}
...
From this information I can deduce that that response's birthday field is of type Date, and deserialize it accordingly. However, things get more complicated if the query uses non-trivial GraphQL features. Take aliasing for example:
query {
allUsers {
name
dayOfBirth: birthday
}
}
At this point I would already need to keep track of any aliasing (which I could do since that information is available if I parse the query), and backtrack those to find the correct type. I fear it might get even more complicated if e.g. fragments are used.
Given that I use graphql-java, and it appears to already need to handle all of these cases for serialization, I wondered if there was an easier way to do this than to manually backtrack the types from the query and schema.
How about generating java classes from the schema and then using those classes to deserialize. There is one plugin which I have used before for this - graphql-java-generator
You may need to enhance the plugin a bit to support your custom scalars though
It basically generates a java client for invoking your GraphQL queries in a Java way.
I had the same problem to deserialize an LocalDate attribute, even using the graphql-java-extended-scalars library.
Researching I found that this library works well for queries but not so well for mutations.
I fixed my problem by customizing SchemaParserOptions, like this:
#Bean
public SchemaParserOptions schemaParserOptions() {
return SchemaParserOptions.newOptions().objectMapperConfigurer((mapper, context) -> {
mapper.registerModule(new JavaTimeModule());
}).build();
}
In the object i didn't use any serialization and deserialization annotations.
So I built the API with a crud on spring boot, the issue arises due to the bidirectional nature of the entities.
I can create it fine manually through the application (non-api) and it appears with children and all.
However, once the API is up, I try to post it (to create) a JSON such as this:
{
"idReserva": 1,
"comentarios": "",
"fechaIngreso": "0019-07-15",
"fechaSalida": "0019-10-30",
"cantidadDePersonas": 3,
"usuario": {
"idUsuario": 1,
"nombres": "test",
"apellidos": "test",
"contrasena": "1234",
"codUsuario": "USU01",
"email": "test#gmail.com",
"foto": ""
},
"pagos": [
{
"idPago": 1,
"tipo": "Efectivo",
"total": 1500
}
],
"habitaciones": [
{
"idHabitacion": 1,
"descripcion": "HabitaciĆ³n Ejecutiva",
"tipo": 3,
"numero": "5",
"codHabitacion": "HAB01",
"precio": "1500 dolares"
}
]
}
The issue comes that in my "create" method inside the repository, I can't receive the nested entities, it does create the "reserve" entry in the database, but it doesn't give it its children
List<Pago> listPagos = new ArrayList<>();
for (Pago pago : reserva.getPagos()){
log.info(pago.getIdPago()+"");
pagoService.create(pago);
listPagos.add(pago);
}
reserva.setPagos(listPagos);
I tried something such as that above to obtain each "pago"(payment) entity from the json and then create it/add it to reserve, since I need it to have the fields of its children payments in the database, but when I log the entities I receive "null" as if it's not receiving anything, is there any specific way I need to obtain the nested entities?
Alright so after a few hours of working around it, I found the issue. the API itself was missing something crucial, when you want to save inside the resource (api) layer, before you actually .save() using the service layer, you want to create an instance of the child entity, using a For: loop pass each entity inside the Json to an instance of that child, and JPA automatically will create them, and add them to the parent entity as well.
Example:
for (Habitacion habitacion : reserva.getHabitaciones()){
habitacion.setReserva(reserva);
}
for (Pago pago : reserva.getPagos()){
pago.setReserva(reserva);
}
Usuario usuario = reserva.getUsuario();
usuario.setReserva(reserva);
(this is inside the createReserva method from the resource layer)
I have a requirement to restrict attributes in the REST response by the caller. Consider the response in JSON format.
Ex:For a given REST endpoint, the default response is like
{
"id" : "111"
"name" : "John"
"age" : "30"
}
For the "caller 1" the response should be like
{
"id" : "111"
"name" : "John"
"age" : "null"
}
For the "caller 2" the response should be like
{
"id" : "111"
"name" : "null"
"age" : "30"
}
In above response JSONs, "null" means, such attributes are not exposed to such callers.
I am looking for a way to implement to control REST response by caller.
The implementation on the server side is heavily dependent on the underlying server technology stack (REST API, DB, User's Role layer, etc.). In some configurations, you defines the data authorization in the DB layer while in other on the REST layer.
Implement field level authorization is a tricky one as not all frameworks provides this granularity.
One framework that do offer such granularity is Jello Framework (I am the author). One of Jello's key features is its inline Authorization Model where you can assign different access levels for data elements at any resolution (Namespaces, Entities, Fields, Actions) and specify who is authorized to access the data via the REST API.
For example -
Let's say you want to expose the 'age' field only to the record owner and the site administrator. In Jello, it will look something like this:
public class Person extends JelloEntity {
#Expose #KeyElement
Integer id;
#Expose
String name;
#Expose({Role.OWNER, Role.ADMIN})
Integer age;
}
I had similar requirement in the past where permissions were granular at field level.
I implemented Rest response writers for each entity. I pass list of fields of that entity for which the calling user has access. Using the list, in the response writer, I used to output only those fields for which the user has access.
I used to output map/list structure from the entity. Jackson then marshall the structure into JSON.
From what I can tell, there are provided means for converting a complex object to proper HAL format. This is of course leveraged in marshalling the objects in the framework itself. Resource and Link objects, etc.
For the sake of a use-case:
Company 1 is an existing Company in my system. I want to add a new Employee that works for Company 1
Below is an example Employee object that you'd receive from a Spring Data REST based service. Spring HATEOAS also provides the means to construct these objects yourself.
{
"id": null,
"firstName": "bZWthNFk",
"lastName": "GtTnrqka",
"loginId": "zTk5rT",
"active": true,
"_links": {
"company": {
"href": "http://localhost/companies/1";
}
}
}
However, this seems to not work for POSTing the object. As I understand it, that same object would have to be POSTed as:
{
"id": null,
"firstName": "bZWthNFk",
"lastName": "GtTnrqka",
"loginId": "zTk5rT",
"active": true,
"company": "http://localhost/companies/1"
}
As far as I can tell, there are no means provided by either the HATEOAS or Data REST project to produce this object for posting to a valid HAL based service, either via RestTemplate or some other means. In fact, I can't find any means of readily POSTing a complex object without some hand-marshalling. Am I wrong in assuming this?
How is one supposed to build out a valid Java SDK for service-to-service communication that leverages HATEOAS principles without this tooling to actually POST objects reliably?
Long story short, I want to post this object without having to hand serialize the URIs for associations.
public class Employee {
private Integer id;
#NotNull
private Company company;
private String firstName;
private String lastName;
}
I've created the following improvement request in reference to this:
https://jira.spring.io/browse/SPR-12678
The approach you suggested should actually work, provided you use at least version 2.0 of Spring Data REST.
You should also have an association resource like http://app.com/employee/10/company. You can PUT a new link to that location using the media type text/uri-list or remove the company from Employee with a DELETE.
UDATE
It seems I didn't address your main concern, that was clarified by your update and comments. So let's take your Employee class that has an association with a Customer.
As you can see from the JSON response you posted, the data structure that the REST API works with doesn't contain a Customer object (or Company in that case), only a link. A client would usually work with the data structure defined by the API. So customerwould be link in the first place and there would be no need for serializing an object to a link.
If the client uses a different data structure internally, then some kind of conversion is necessary anyway. But the reason would be the different structure, not HAL or association links.