I'm learning GraphQL in spring boot and I have created a mutation that saves used details in database. When I try to call the mutation from GraphiQL UI using following mutation query, it throws NullPointerException because the object I passed in mutation is not mapped with UserDto and I don't know why.
I have following code:
Controller
#Controller
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#QueryMapping(name = "userById")
public UserDto findUserById(#Argument Long id) {
return userService.findById(id);
}
#QueryMapping
public String removeUserById(#Argument Long id) {
return userService.removeById(id);
}
#MutationMapping(name = "save")
public UserDto save(#Argument UserDto user) {
return userService.save(user);
}
#QueryMapping(name = "users")
public List<UserDto> findAll() {
return userService.findAll();
}
}
GraphQL Schema
schema {
query: Query
mutation: Mutation
}
type Query{
users:[User]
userById(id:ID):User
}
type Mutation{
save(userInput:UserInput):User
}
#input types
input UserInput{
firstName:String!
lastName:String!
emailAddress:String!
ipAddress:String!
address:AddressInput!
}
input AddressInput{
addressLine1:String!
addressLine2:String!
addressLine3:String!
addressLine4:String!
addressLine5:String!
addressPostCode:Int!
}
#object types
type User{
firstName:String!
lastName:String!
emailAddress:String!
ipAddress:String!
address:Address!
}
type Address{
addressLine1:String!
addressLine2:String!
addressLine3:String!
addressLine4:String!
addressLine5:String!
addressPostCode:Int!
}
Mutation Query
mutation Save($userDto: UserInput!) {
save(userInput: $userDto) {
firstName
lastName
emailAddress
ipAddress
address {
addressLine1
addressLine2
addressLine3
addressLine4
addressLine5
addressPostCode
}
}
}
Variables
{
"userDto": {
"ipAddress": "192.168.0.124",
"firstName": "John",
"lastName": "Mark",
"emailAddress": "john#gmail.com",
"address": {
"addressLine1": "251 WC",
"addressLine2": "UC MAIN",
"addressLine3": "PB121",
"addressLine4": "New York",
"addressLine5": "USA",
"addressPostCode": 457821
}
}
}
Results
{
"errors": [
{
"message": "INTERNAL_ERROR for b6287602-fc10-6ecf-2091-b57ceaeb9f0a",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"save"
],
"extensions": {
"classification": "INTERNAL_ERROR"
}
}
],
"data": {
"save": null
}
}
When I run the GraphQL query, it throws NullPointerException in console.
Console Error
java.lang.NullPointerException: Cannot invoke "com.graphql.sample.dto.UserDto.getEmailAddress()" because "userDto" is null
I'm answering my question because I have already solved it. I had made a small mistake in controller near #Argument UserDto user as below:
#MutationMapping(name = "save")
public UserDto save(#Argument(name = "userInput") UserDto user) {
return userService.save(user);
}
The name in #Argument should match the name of parameter in Mutation type as below:
type Mutation{
save(userInput:UserInput):User
}
type User{
firstName:String!
lastName:String!
emailAddress:String!
ipAddress:String!
address:Address!
}
Reason 1:
Your save mutation result is returned to this entity. But it can be possible that a property that you are receiving might be null that's why this error is thrown, as you are using ! with each property. You can remove ! if a value can/is null
Reason 2:
I couldn't see your resolver so it might be possible that the result is not an object or the property names that you are receiving don't match with the property names inside type User. It should be same or use resolver to provide their values
Related
I have a request where there is multple object in the json file . I need to add them via PostMan
This is my Dto
public class CustomerDto
{
[Required(ErrorMessage = "The Id is required")]
public int Id { get; set; }
[Required(ErrorMessage = "The FirstName is required")]
public string FirstName { get; set; }
}
This is my ServiceClass, this is where i add it to a class level customerlist object for now.
public static class CustomerService
{
public static List<CustomerDto> customerList = new List<CustomerDto>() {
new CustomerDto { FirstName = "Joe", Id = 1},
new CustomerDto { FirstName = "Rose", Id = 2},
new CustomerDto { FirstName = "Sid", Id = 3 },
};
}
This is my Api Post Request
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public ActionResult<VillaDTO> addCustomer([FromBody]CustomerDTO customerDTO)
{
if (customerDTO== null)
{
return BadRequest(villaDTO);
}
CustomerService.customerList.Add(customerDTO);
return Ok(customerDTO);
}
I try to debug but before it hits the method call it throws the exceptions. I just want to know how my object is populated first.
Thanks for having a look
After updating a bad Json using Json Validator . I now get this error message.
DTO seems to be the issue .
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-58f71fb73f3af573db60c11551cb2093-792cd201f9e3f0d3-00",
"errors": {
"$": [
"The JSON value could not be converted to CustomerManagementAPI.Model.Dto.CustomerDto. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
],
"customerDTO": [
"The customerDTO field is required."
]
}
}
If you're trying to post a collection
[
{
"firstName": "John",
"id": 6
},
{
"firstName": "Anna",
"id": 7
}
]
Have your action accept a collection as well
public IActionResult addCustomer([FromBody] List<CustomerDto> customers) { }
I need to validate a JSON before saving the information on the database. The object has the following pattern:
{
"id": "string",
"name": "string",
"description": "string",
"price": "int"
}
If the post was made with the correct JSON, I return the object saved (The ID can't be in the post, it will be generated by database).
But, if the post has a missing field or additional one I need to return an error message.
How is the best way to do this? I'm reading some articles and can't understand exactly.
I'm using spring-boot, JPA, connecting to an H2 database (MVC Pattern).
Here is my Controller and Service layer:
First, the delete method. That works as I want.
#DeleteMapping("/{id}")
#Transactional
public ResponseEntity<Product> deleteProduct(#PathVariable String id) {
ResponseEntity<Product> response = productService.deleteProduct(id);
return response;
}
public ResponseEntity<Product> deleteProduct(String id) {
Optional<Product> product = productRepository.findById(id);
if (product.isPresent()) {
productRepository.deleteById(id);
return ResponseEntity.ok().build();
} else
return ResponseEntity.notFound().build();
}
Here the Post Method. That I can't find a good way to return the response that I want:
#PostMapping
public ResponseEntity<Product> insertProduct(#RequestBody ProductForm form) {
Product product = productService.insertProduct(form);
if (product) {
return ResponseEntity.ok().body(product);
} else
return ResponseEntity.notFound().build();
}
public Product insertProduct(ProductForm form) {
Product product = form.insertConverter();
productRepository.save(product);
return product;
}
The second method in both snippets is located in a separated JavaClass.
This was what I want to return:
{
"code": integer,
"error": "string"
}
I have a MemberController that has two GetMappings, one returns a paginated list of members and the other returns a member. I have a MemberModelAssembler which overrides toModel and returns a selfRel() link. How to make the toModel method in the MemberModelAssembler return a the pagination link for each member? Given I cannot pass Pageable and PagedResourcesAssembler to the MemberModelAssembler?
Expected result when calling api/v1/member/1
{
"id": 1,
"phone": "85298890006",
"profileImageUrl": null,
"displayedName": "Mak",
"salutation": "MS",
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/member/1"
}
*****Want to achieve this*****
"members": {
"href": "http://localhost:8080/api/v1/memberpage=0&size=20"
*****Want to achieve this*****
}
}
My MemberController:
#RestController
#RequestMapping("api/v1/member")
class MemberController(
private val service: MemberService,
private val assembler: MemberModelAssembler
) {
#GetMapping
fun findAll(
pageable: Pageable,
pagedResourcesAssembler: PagedResourcesAssembler<Member>
): ResponseEntity<PagedModel<EntityModel<Member>>> {
val members = service.findAll(pageable)
return ResponseEntity(pagedResourcesAssembler.toModel(members, assembler), HttpStatus.OK)
}
#GetMapping("/{id}")
fun findById(#PathVariable id: Int): ResponseEntity<EntityModel<Member>> {
val member = service.findById(id) ?: throw ItemNotFoundException(this::class.simpleName!!, id)
return ResponseEntity(assembler.toModel(member), HttpStatus.OK)
}
}
My MemberModelAssembler
#Component
class MemberModelAssembler : RepresentationModelAssembler<Member, EntityModel<Member>> {
override fun toModel(member: Member) =
EntityModel.of(
member,
linkTo(methodOn(MemberController::class.java).findById(member.id)).withSelfRel(),
)
}
For anyone who still has a problem with that:
Although it's a little counteractive, you can enter a 3rd parameter as the initial link.
Hello i have springboot with mongodb (spring-boot-starter-data-mongodb)
My problem is if I send only one or only the fields I want to change so the other values are set to null. I found something on the internet like #DynamicUpdate but not working on mongodb can you help me with this problem. I'm a beginner, I don't know how to help and it's quite important for me, if you need more code or more information, I'll write in the comment. I hope I have described the problem sufficiently. :)
MY POJO:
#Data
#Getter
#Setter
#NoArgsConstructor
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
private boolean enabled;
private String note;
Repo
#Repository
public interface PersonRepository extends MongoRepository <Person, String> {
}
i have this call
#PutMapping("/{id}")
#ResponseBody
public void UpdatePerson (#PathVariable String id , #RequestBody Person person) {
personRepository.save(person);
}
#GetMapping(path = "/{id}")
public Person getPersonByid(#PathVariable String id ){
return personRepository.findById(id).orElseThrow(PersonNotFound::new);
}
sample:
get call before update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Rambo",
"lastName": "Norris",
"enabled": true,
"note": "hello this is my first note from you",
}
put call :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck"
}
get call after update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck",
"lastName": null,
"enabled": false,
"note": null,
}
what I would like
get call before update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Rambo",
"lastName": "Norris",
"enabled": true,
"note": "hello this is my first note from you",
}
put call :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck"
}
get call after update :
{
"id": "5fc940dc6d368377561dbb02",
"firstName": "Chuck",
"lastName": "Norris",
"enabled": true,
"note": "hello this is my first note from you",
}
You are inserting a new collection instead of updating. First, you need to get the old value from mongodb, then you need to update the collection, then save to DB.
Use the below code in #putmapping.
#PutMapping("/{id}")
#ResponseBody
public void UpdatePerson (#PathVariable String id , #RequestBody Person person) {
Person personFromDB = personRepository.findById(person.getId());
personFromDB.setFirstName(person.getFirstName());
personRepository.save(personFromDB);
}
Try updating like this
#PutMapping("/{id}")
public ResponseEntity<Person> UpdatePerson (#PathVariable String id , #RequestBody
Person person) {
Optional<Person> personData = personRepository.findById(id);
if (personData.isPresent()) {
Person _tutorial = personData.get();
if(!StringUtils.isEmpty(person.getFirstName())) {
_tutorial.setFirstName(person.getFirstName());
}
if(!StringUtils.isEmpty(person.getLastName())) {
_tutorial.setLastName(person.getLastName());
}
if(!StringUtils.isEmpty(person.getNote())) {
_tutorial.setNote(person.getNote());
}
if(!StringUtils.isEmpty(tutorial.isEnabled())) {
_tutorial.setEnabled(tutorial.isEnabled());
}
return new ResponseEntity<>(repo.save(_tutorial), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
I recently started reviewing/learning some about reactive couchbase and I'm trying to implement a sample using that technology with spring data and spring boot, I have the following models:
#Document
public class Person {
#Id
private String id;
#Field
private String name;
#Field
private String lastName;
#Field
private List<Address> address;
// Getters & Setters
}
#Document
public class Address {
#Id
private String code;
#Field
private String mainStreet;
#Field
private String secondStreet;
// Getters & Setters
}
Repositories:
#N1qlPrimaryIndexed
#ViewIndexed(designDoc = "address")
public interface AddressRepository extends ReactiveCouchbaseRepository<Address, String> {
}
#N1qlPrimaryIndexed
#ViewIndexed(designDoc = "person")
public interface PersonRepository extends ReactiveCouchbaseRepository<Person, String> {
}
Service:
public interface PersonService {
Flux<Person> getAllPersons();
Mono<Person> getPerson(String id);
Flux<Address> getAddressesByPerson(String id);
}
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonRepository personRepository;
#Override
public Flux<Person> getAllPersons() {
return personRepository.findAll();
}
#Override
public Mono<Person> getPerson(String id) {
return personRepository.findById(id);
}
#Override
public Flux<Address> getAddressesByPerson(String id) {
return null;
}
}
Controller:
#RestController
#RequestMapping("/sample_couchbase")
public class PersonController {
#Autowired
private PersonService personService;
#GetMapping("/people")
public Flux<Person> getAllPersons() {
return personService.getAllPersons();
}
#GetMapping("/person/{id}")
public Mono<ResponseEntity<Person>> getPersonsById(#PathVariable String id) {
return personService.getPerson(id)
.map(person -> ResponseEntity.status(HttpStatus.OK).body(person))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
}
As so far this works well, I can retrieve all the people and also filter by an id of a specific person, on the other hand I would like to retrieve all list of addresses of a specific person, I mean I have this document in couchbase:
{
"id": "1",
"name": "Scooby",
"lastName": "Doo",
"address": [
{
"code": "A1",
"mainStreet": "AAA",
"secondStreet": "BBB",
"phone": "11111",
"place": "home"
},
{
"code": "A2",
"mainStreet": "CCC",
"secondStreet": "DDD",
"phone": "22222",
"place": "work"
},
{
"code": "A3",
"mainStreet": "EEE",
"secondStreet": "FFF",
"phone": "33333",
"place": "treasury"
}
],
"classType": "com.jcalvopinam.model.Person"
}
When I call the service e.g.: http://localhost:8080/sample_couchbase/person/1/addresses I want to get this:
"address": [
{
"code": "A1",
"mainStreet": "AAA",
"secondStreet": "BBB",
"phone": "11111",
"place": "home"
},
{
"code": "A2",
"mainStreet": "CCC",
"secondStreet": "DDD",
"phone": "22222",
"place": "work"
},
{
"code": "A3",
"mainStreet": "EEE",
"secondStreet": "FFF",
"phone": "33333",
"place": "treasury"
}
]
I imagined create in the repository a method something like this:
Mono<Person> findById(String id);
And in the service layer I imagine getting the whole object and then filtering by Address, but this is something different from what I'm used to doing, now I'm using Mono and Flux and not a simple object or a List, so I don't know how to do that, can someone give me an idea? or is there a better solution?
I found an easy way to retrieve the Addresses of a specific Person, I used a query where I specified what I want to retrieve:
#Query("SELECT addresses, META(sample).id as _ID, META(sample).cas as _CAS FROM `sample` WHERE id = $1")
Flux<Person> findAddressesByPerson(String id);
I noticed that it is necessary to specify the META(sample).id as _ID and META(sample).cas as _CAS attributes to avoid the following error:
org.springframework.data.couchbase.core.CouchbaseQueryExecutionException: Unable to retrieve enough metadata for N1QL to entity mapping, have you selected _ID and _CAS?; nested exception is rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: com.couchbase.client.java.query.DefaultAsyncN1qlQueryRow.class
If you want to take a look at the implementation in more detail, here is the sample: https://github.com/juanca87/sample-couchbase-rx