Spring REST API, custom entity fields in the response - java

I'm working in a REST API (Java, SpringBoot, QueryDsl...) and I would like to customize the fields that I have in the result. By default I'm obtaining all fields of the entity but the fields that I need depends on the request. This should be something dynamic.
As far as I know, using projections I can obtain something like this but I have to declare previously the projection and I would like to work with something dynamic not static. On the other hand I have seen than something like GraphQL allows this behaviour but I would have to refactor everything.
Has anyone had this problem before?
This is my code:
BaseRestCRUDController
public abstract class BaseRestCRUDController<T extends EntityBase, V extends BaseDTO> {
#GetMapping("/list")
public ResponseEntity<List<V>> findAll(Predicate predicate, Pageable pageable) {
log.info("FindAll");
return new ResponseEntity(getCRUDService().findAll(predicate, pageable), HttpStatus.OK);
}
}
ExampleController
#RestController
#RequestMapping("/api/example")
public class ExampleController
extends BaseRestCRUDController<Example, ExampleDTO> {
#Autowired
private ExampleService ExampleService;
#Override
public ResponseEntity<List<ExampleDTO>> findAll(
#QuerydslPredicate(root = Example.class) Predicate predicate, Pageable pageable) {
return super.findAll(predicate, pageable);
}
#Override
protected CRUDService<Example, ExampleDTO> getCRUDService() {
return ExampleService;
}
}
Example (Entity)
public class Example {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "creation_date")
private Instant creationDate;
#Column(name = "last_update")
private Instant lastUpdate;
#Column(name = "erasure_date")
private Instant erasureDate;
}
http://localhost:8080/api/example/list?name=test&page=0&size=5&sort=id,desc
http://localhost:8080/api/example/list?name=foo&page=1&size=10&sort=id,desc
http://localhost:8080/api/example/list?page=0&size=2&sort=id,asc
[
{
"id": 1,
"name": "e1",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
},
{
"id": 2,
"name": "e2",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
},
{
"id": 3,
"name": "e3",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
}
]
How can I obtain something like this without use projections?
http://localhost:8080/api/example/list?fields=id,name&page=1&size=10&sort=id,desc
[
{
"id": 1,
"name": "e1"
},
{
"id": 2,
"name": "e2"
},
{
"id": 3,
"name": "e3"
}
]
http://localhost:8080/api/example/list?fields=name&page=1&size=10&sort=id,desc
[
{
"name": "e1",
},
{
"name": "e2",
},
{
"name": "e3",
}
]

#Ignore
private Instant creationDate;
Try this.
You can use #Ignore on getter,setter or fields.

Related

How to implement group by with sum of multiple fields in java

Controller class:
#RestController
#RequestMapping(value = "/api/v1/")
public class TestController {
#Autowired
TestService testService;
#GetMapping(value = "test")
public List<Bean> getDetails() {
return testService.getDetails();
}
}
Service class:
#Service
public class TestService {
#Autowired
BeanRepository beanRepository;
public List<Bean> getDetails() {
List<Bean> res = new ArrayList<>();
try{
List<BeanEntity> beans = beanRepository.findAllApplications();
for(BeanEntity beanEntity : beans) {
Bean bean = new Bean();
BeanUtils.copyProperties(beanEntity, bean);
if(beanEntity.getDesc().contains("Motor") || beanEntity.getDesc().contains("Nut")){
bean.setName("A");
} else if (beanEntity.getDesc().contains("Bolt") || beanEntity.getDesc().contains("Engine")) {
bean.setName("B");
} else {
bean.setName("C");
}
res.add()
}
} catch(Exception e) {
throw new e;
}
}
}
Entity class:
#Getter
#Setter
#NoArgConstructor
#AllArgConstructor
#Entity
public class BeanEntity implements Serializable {
private static final long serialVersionID = 3269743987697633760L;
#Id
#Column(name = "ApplicationID", nullable = false, unique = true)
public String application_id;
#Column(name= "Description")
public String desc;
#Column(name = "Status")
public String status;
#Column(name ="Approved")
public int approved;
#Column(name ="Declined")
public int declined;
}
Bean class:
#Getter
#Setter
#NoArgConstructor
#AllArgConstructor
public class Bean {
public String application_id;
public String desc;
public String status;
public int approved;
public int declined;
public String name;
}
Repository class:
#Repository
public interface BeanRepository extends JpaRepository<BeanEntity, String> {
#Query(name = "select * from abc", nativeQuery = true)
List<BeanEntity> findAllApplications();
}
Below is the Current Response i am getting from above code:
[
{
"application_id" : "12345",
"desc": "Motor",
"status": "active",
"approved": 1,
"declined": 0,
"name": "A"
},
{
"application_id" : "14785",
"desc": "Nut",
"status": "active",
"approved": 0,
"declined": 1,
"name": "A"
},
{
"application_id" : "15974",
"desc": "Bolt",
"status": "not-active",
"approved": 1,
"declined": 0,
"name": "B"
},
{
"application_id" : "36985",
"desc": "Engine",
"status": "active",
"approved": 1,
"declined": 0,
"name": "B"
},
{
"application_id" : "78945",
"desc": "Filter",
"status": "active",
"approved": 1,
"declined": 0,
"name": "C"
},
{
"application_id" : "45612",
"desc": "AC",
"status": "active",
"approved": 0,
"declined": 1,
"name": "C"
}
]
Expected Response i am looking for:
[
{
"name": "A",
"approved": 1,
"declined": 1
},
{
"name": "B",
"approved": 2,
"declined": 0
},
{
"name": "C",
"approved": 1,
"declined": 1
}
]
I am in beginner phase of learning java, How to implement group by with sum of multiple fields in java.
Can someone help me how i can achieve the expected response.

How to add a list to an object in Spring

I have this object class that has a list of customers as an attribute:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class PeopleDTO {
private String processType;
private String operation;
private String entity;
private String entityType;
private Long id;
private Document document;
#Getter
#Setter
class Customer {
private String systemId;
private String customerId;
}
private List<Customer> customers;
}
This list is retrieved calling another microservice using webclient as follows:
public Mono<CuCoPerson> getCuCoPerson(Integer cucoId, String GS_AUTH_TOKEN) {
WebClient webClient = WebClient.create();
return webClient.get()
.uri(GET_RELATION_BY_ID + cucoId)
.header("Accept", "application/json")
.header("Authorization", GS_AUTH_TOKEN)
.retrieve()
.bodyToMono(CuCoPerson.class)
.map(cuCoPerson -> {
List<CustomerRelation> matches = cuCoPerson.getRelatedCustomers()
.stream()
.filter(relation -> relation.getSystemId().equals(400) || relation.getSystemId().equals(300) || relation.getSystemId().equals(410))
.filter(relation -> relation.getCustomerId().contains("F"))
.collect(Collectors.toList());
cuCoPerson.setRelatedCustomers(matches);
return cuCoPerson;
});
}
This method return a cucoPerson as follows:
{
"id": 1,
"relatedCustomers": [
{
"customerId": "xxx",
"systemId": 999
}
]
}
So now I want to add this object to my PeopleDTO class, but I don't know how. This is what I've done son far (hardcoded):
public PeopleDTO createPeople(Long id) {
PeopleDTO people = new PeopleDTO();
people.setProcessType("ONLINE");
people.setOperation("UPDATE");
people.setEntity("DOCUMENT");
people.setEntityType("DOCUMENT");
people.setIdCuco(id);
people.setDocument(new Document());
people.setCustomers(......);
}
So as you can see I don't know how to add a Mono in the last line.
The expected result should be like this:
{
"type": "ONLINE",
"operation": "UPDATE",
"id": 1,
"entity": "DOCUMENT",
"entityType": "NIE",
"documents": {
"id": 1,
"additionals": {
"issuing_authority": "Spain",
"country_doc": "ES",
"place_of_birth": "",
"valid_from": "1995-08-09",
"valid_to": "0001-01-01"
},
"code": "X12345",
"typeDocument": "NIE"
},
"id": 1,
"relatedCustomers": [
{
"customerId": "xxx",
"systemId": 999
}
]
}
first, create a list of customers like:
List<Customer> customers=new ArrayList<>;
Then add all the Customers to it one by one using a loop,
then you can directly add that to your object like
people.setCustomers(customers);
your object assignment should look something like:
public PeopleDTO createPeople(Long id) {
PeopleDTO people = new PeopleDTO();
people.setProcessType("ONLINE");
people.setOperation("UPDATE");
people.setEntity("DOCUMENT");
people.setEntityType("DOCUMENT");
people.setIdCuco(id);
people.setDocument(new Document());
List<Customer> customers=new ArrayList<>;
//add data to customer
people.setCustomers(customers);
}

how can i access nested array from mongoDB collection using spring boot?

I am writing a credit card management system. This is the DB for the project (each user can have many cards and each card can have many transactions)
DB code sample:
{
"_id": 1,
"name": "Jawad",
"lastname": "Zakhour",
"username": "jwdzkh",
"password": "pass123",
"cards": [{
"cardnumber": "1234-5678-9123-4567",
"cardholdername": "yolla kazan",
"expirationdate": "05/09/2021",
"cvv": "256",
"type": "Credit",
"creditlimit": "3500",
"transactions": [{
"date": "03/06/2020",
"amount": 750,
"receiptNo": "EAC-15-123-45678"
}, {
"date": "06/08/2020",
"amount": 320,
"receiptNo": "THY-18-568-5866"
}]
}, {
"cardnumber": "4589-3256-7841-9655",
"cardholdername": "nabil dagher",
"expirationdate": "06/07/2022",
"cvv": "365",
"type": "Debit",
"balance": "5200",
"transactions": [{
"date": "09/11/2019",
"amount": 90,
"receiptNo": "TYH-35-163-5896"
}, {
"date": "10/10/2020",
"amount": 120,
"receiptNo": "NJU-85-586-4287"
}]
}, {
"cardnumber": "8976-3154-3187-3659",
"cardholdername": "jawad zakhour",
"expirationdate": "06/07/2022",
"cvv": "365",
"type": "Debit",
"balance": "12000",
"transactions": [{
"date": "01/02/2018",
"amount": 14,
"receiptNo": "DFG-58-348-9863"
}, {
"date": "04/12/2019",
"amount": 550,
"receiptNo": "FGH-46-008-3478"
}]
}]
}
on java spring boot I have three models User, Card, Transaction.
#Data
#AllArgsConstructor
#Document(collection = "Vault")
public class Card {
private String cardnumber;
private String cardholdername;
private String expirationdate;
private String cvv;
private String type;
private String creditlimit;
private String balance;
private List<Transaction> transactions;
}
#Data
#AllArgsConstructor
#Document(collection = "Vault")
public class User {
#Id
private int id;
private String name;
private String lastname;
private String username;
private String password;
private List<Card> cards;
}
#Data
#AllArgsConstructor
public class Transaction {
private String date;
private int amount;
private String receiptNo;
}
and also I have created 2 repositories CardsRepository and UserRepository
How can I Return all cards of a specific user?
now i want to get all the transactions for a specific card how is that possible?
since every card has a list it should be possible..
To get transactions of a specific card, just use filter(<some_predicate>). For example,
val user = userRepository.findById(id).orElseThrow(() -> UserNotFoundException(id));
val transactions = user.getCards()
.stream()
.filter(card -> card.getCardNumber().equals("1111.."))
.findFirst()
.map(Card::getTransactions)
.orElseGet(Collections::emptyList);
Here, if a specific card that matches the predicate that cardNumber equals some input, it will be passed on to the next operation in the stream pipeline and findFirst() returns the first occurrence of such card.
Edit with request:
#GetMapping("/users/{username}/cards/{cardnumber}/transactions")
public List<Transaction> getTransactionByCardnumber(#PathVariable String username, #PathVariable String cardnumber) {
val user = userRepository.findById(id).orElseThrow(() -> UserNotFoundException(id));
val transactions = user.getCards()
.stream()
.filter(card -> card.getCardnumber().equals(cardnumber))
.findFirst()
.map(Card::getTransactions)
.orElseGet(Collections::emptyList);
}

How map List to an Object with list in mapstructs

How can I use MapStruct to create a mapper that maps a list (my source) to a object with a list (destination)?
My source classes looks like this:
class SourceB {
private String name;
private String lastname;
}
class SourceA {
private Integer id;
private List<SourceB> bs;
}
so I need to transform it to this:
class DestinationA {
private Integer id;
private DestinationAB bs;
}
class DestinationAB {
private List<DestinationB> b;
}
class DestinationB {
private String name;
private String lastname;
}
Expected sample json:
source:
{
"id": 1,
"bs": [
{
"name": "name1",
"lastname": "last1"
},
{
"name": "name2",
"lastname": "last2"
}
]
}
destination:
{
"id": 1,
"bs": {
"b": [
{
"name": "name1",
"lastname": "last1"
},
{
"name": "name2",
"lastname": "last2"
}
]
}
}
It's quite simple. Just put #Mapping annotation with specified source and destination on top of the mapping method.
#Mapper
public interface SourceMapper {
#Mapping(source = "bs", target = "bs.b")
DestinationA sourceAToDestinationA(SourceA sourceA);
}

katharsis collection of non primitives serialization

Trying to serialize a collection of non-primitive types using katharsis, but getting an empty collection all the time.
Response example:
{
"data": {
"type": "products",
"id": "1",
"attributes": {
"simpleAttributes": [
{}
],
"variationGroup": "variationGroup"
},
"relationships": {},
"links": {
"self": "http://localhost:8080/api/products/1"
}
},
"included": []
}
Expected response:
{
"data": {
"type": "products",
"id": "1",
"attributes": {
"simpleAttributes": [
{
tittle: "some title",
value: "some value"
}
],
"variationGroup": "variationGroup"
},
"relationships": {},
"links": {
"self": "http://localhost:8080/api/products/1"
}
},
"included": []
}
Domain objects (getters, setters, constructor and other stuff omitted by using lombok #Data annotation):
#JsonApiResource(type = "products")
#Data
public class Product {
#JsonApiId
private Integer id;
private List<SimpleAttribute> simpleAttributes = new ArrayList<>();
private String variationGroup;
}
#Data
public class SimpleAttribute implements Serializable{
private String title;
private String value;
}
I do not want to use relationships in this case or to include attributes to "included" field. Is it possible in katharsis?
Not sure what actually was wrong, but the problem disappeared after I changed katharsis-spring version from 2.3.0 to 2.3.1.

Categories

Resources