Capturing ONLY sent fields in the Request body in Spring Boot PATCH rest API - spring

I am trying to implement a PATCH API as follows:
#PatchMapping("/student)
public ResponseEntity<StudentDTO> patchStudent(#RequestBody StudentDTO studentDTO)
throws URISyntaxException {
...
}
Here StudentDTO is as follows:
class StudentDTO {
String name,
String rollNum,
String grade,
String id,
...
}
Here in PATCH API, user may send any number of fields including id as follows:
Request 1:
{
id: 1,
name: "Test"
}
Request 2:
{
id:1,
name: "Test",
rollNumber : "123456"
}
Request 3:
{
id:1,
name: "Test",
rollNumber : "123456",
grade : null. //NOTE: user may send null as well for a field in request body
}
I a not getting how should I capture only those fields in the request body while patching the data in the backend?

Related

Call GraphQL api from Rest endpoint in spring boot returning null value

From my rest endpoint I am trying to call 3rd party graphql api but in response I am getting null values.
Request:
{
"query": "query($id: String!) { hiringOrganization (id: $id) { name } }",
"variables": {
"id": "seekAnzPublicTest:organization:seek:93WyyF1h"
}
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"hiringOrganization": {
"name": "Acme Corp"
}
}
}
Inside the resource folder I have placed Query and variable request:
query($id: String!) { hiringOrganization (id: $id) { name } }
Variables:
{
"id": "hiringId"
}
From Postman I am getting the below response:
[![enter image description here][3]][3]
Calling the Query and variables from resource folder:
public static String getSchemaFromFileName(final String fileName) throws IOException {
if(fileName.contains("Variables")){
log.info("inside variables::"+fileName);
return new String(
GraphqlSchemaReaderUtil.class.getClassLoader()
.getResourceAsStream("seekGraphQL/variables/"+fileName+".graphql")
.readAllBytes()
);
} else {
log.info("inside query::"+fileName);
return new String(
GraphqlSchemaReaderUtil.class.getClassLoader()
.getResourceAsStream("seekGraphQL/query/" + fileName + ".graphql")
.readAllBytes()
);
}
}
Calling the graphql endpoint:
public Mono<HiringDTO> getHirerDetails(final String id) throws IOException {
GraphqlRequestBody requestBody=new GraphqlRequestBody();
final String query= GraphqlSchemaReaderUtil.getSchemaFromFileName("hiringQuery");
final String variables= GraphqlSchemaReaderUtil.getSchemaFromFileName("hiringVariables");
log.info("before id::"+id);
requestBody.setQuery(query);
requestBody.setVariables(variables.replace("hiringId",id));
log.info("id::"+id);
return webclient.post()
.uri(url)
.bodyValue(requestBody)
.retrieve()
.bodyToMono(HiringDTO.class);
}
My rest end point is a post request which has hiring Id in request body . I don't know why I am getting the null values. Please assist me to find out the issues.
Postman request/response:

AWS Appsync schema query resolver response mapping

After performing a create mutation, i expect to get the entire response object as shown in
https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html
But all i manage to get is the auto generated id (part of the schema) which is of uuid type.
Below is my schema and response mapping :-
Schema:-
type Apple{
id: ID!
type: String
price: Float
}
Mutation used :-
mutation MyMutation {
updateApple(input: {id: "0a9aa23f-3017-4b67-8dcd-354ef8f609d8", type: "green", price: 10.5}) {
id
}
}
======================================================================
type Mutation {
updateApple(input: UpdateAppleInput!): Apple
}
======================================================================
input UpdateAppleInput {
id: ID!
type: String
price: Float!
}
======================================================================
Response mapping template which returns only id
## Raise a GraphQL field error in case of a datasource invocation error
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
Sample response below (I am expecting the updated "Apple" object):-
{
"data": {
"updateApple": {
"id": "0b8bb23f-5613-4b67-8dcd-354ef8f617r5"
}
}
}
I am referring the example given in :
https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-rds-resolvers.html#run-queries
And i specifically need the entire Apple object as response when i perform create or update mutations..
EDIT 1: Below response template works fine with a query. Why is it not working with a mutation? :
$utils.toJson($utils.rds.toJsonObject($ctx.result)

passing json in json to spring controller

I am trying to pass json object to spring controller and I manage to do that, but value of one property is in json and I think that I have problem because of it. But there is no other way to pass that data. Code is below,
data class:
#Entity
data class Section(
#Id
#GeneratedValue
val id: Long = 0L,
val name: String = "",
var text: String,
#ManyToOne
var notebook: Notebook
)
Controller code:
#PutMapping("/sections/{id}")
fun updateSection(#RequestBody section: Section, #PathVariable id: Long): Section =
sectionRepository.findById(id).map {
it.text = section.text
it.notebook = section.notebook
sectionRepository.save(it)
}.orElseThrow { SectionNotFoundException(id) }
javascript sending post to api:
function updateApi(data) {
axios.put(MAIN_URL + 'sections/' + data.id, {
data
})
.then(showChangesSaved())
.catch(ShowErrorSync());
}
function saveSection() {
var data = JSON.parse(window.sessionStorage.getItem("curr-section"));
data.text = JSON.stringify(element.editor).toString();
updateApi(data);
}
I get error like this:
2020-11-18 15:06:24.052 WARN 16172 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Instantiation of [simple type, class org.dn.model.Section] value failed for JSON property text due to missing (therefore NULL) value for creator parameter text which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class org.dn.model.Section] value failed for JSON property text due to missing (therefore NULL) value for creator parameter text which is a non-nullable type
at [Source: (PushbackInputStream); line: 1, column: 375] (through reference chain: org.dn.model.Section["text"])]
so text in element.editor is JSON formatted string and I need to pass it as it is to controller. Is there any way to do that? I tried searching, but I can't find json in json help...
Whole project is available on github
What does your json looks like? If I check out your project and run the following two tests:
one with Section as an object as request body
one with Section as json
Both will succeed. So the problem might lie in your JSON:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class HttpRequestTest {
#LocalServerPort
private val port = 0
#Autowired
private val restTemplate: TestRestTemplate? = null
#Test
fun sectionAsObject() {
val section = Section(0L, "2L", "text", Notebook(1L, "1", "2"))
assertThat(restTemplate!!.put("http://localhost:$port/sections/123", section
)).isNotNull
}
#Test
fun sectionAsJson() {
val sectionAsJson = """
{
"id": 0,
"name": "aName",
"text": "aText",
"noteBook": {
"id": 0,
"name": "aName",
"desc": "2"
}
}
""".trimIndent()
assertThat(restTemplate!!.put("http://localhost:$port/sections/123", sectionAsJson
)).isNotNull
}
}
BTW: it is not a pretty good habit to expose your database ids, which is considered to be a security risk as it exposes your database layer. Instead, you might want to use a functional unique key ;)

How I can return my custom json file instead of default json file that generates spring boot?

I have a rest controller for authorization:
#RestController
class AuthController {
#PostMapping("/sign-up")
fun signUp(#RequestBody signUpRequest: SignUpRequest): ResponseEntity<String> {
some code here..
}
}
The signUp method gets SignUpRequest model as a request body. SignUpRequest model is:
enum class Role {
#JsonProperty("Student")
STUDENT,
#JsonProperty("Tutor")
TUTOR
}
data class SignUpRequest(
val role: Role,
val email: String,
val password: String
)
When I make /sign-up post request with JSON:
{
"role": "asdf",
"email": "",
"password": ""
}
It returns me an answer that were generated by spring boot:
{
"timestamp": "2020-02-12T05:45:42.387+0000",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Cannot deserialize value of type `foo.bar.xyz.model.Role` from String \"asdf\": not one of the values accepted for Enum class: [Student, Tutor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `foo.bar.xyz.model.Role` from String \"asdf\": not one of the values accepted for Enum class: [Student, Tutor]\n at [Source: (PushbackInputStream); line: 3, column: 10] (through reference chain: foo.bar.xyz.model.SignUpRequest[\"role\"])",
"path": "/sign-up"
}
Question is: How I can return my custom JSON instead of that default generated JSON?
I want to return my custom JSON, like:
{
"result": "Invalid user data are given",
"errors": [
{
"fieldName": "ROLE",
"text": "Given role does not exist"
},
{
"fieldName": "EMAIL",
"text": "EMAIL is empty"
}
]
}
I suggest you to create ErrorContrller that generates custom json map as response. Then when you will catch an error in sign-up method, call ErrorContrllers method.
You can find info from this link
Finally I found out a solution. You should create a class that annotates #ControllerAdvice, and make a method that annotates #ExceptionHandler.
#ControllerAdvice
class HttpMessageNotReadableExceptionController {
#ExceptionHandler(HttpMessageNotReadableException::class)
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
fun handleException(
exception: HttpMessageNotReadableException
): PostSignUpResponseError {
val errors = mutableListOf<PostSignUpResponseErrorItem>()
errors.add(
PostSignUpResponseErrorItem(
fieldNamePost = "Role",
text = "Given role does not exist"
)
)
return PostSignUpResponseError(
result = "Invalid user data are given",
errors = errors
)
}
}
where PostSignUpResponseErrorItem and PostSignUpResponseError are:
data class PostSignUpResponseError(
val result: String,
val errors: List<PostSignUpResponseErrorItem>
)
class PostSignUpResponseErrorItem(
val fieldNamePost: PostSignUpRequestFieldName,
val text: String
)
Anyway, I still don't know how to attach this thing to a certain PostMapping method.

Getting 415 Unsupported Media Type when trying to send request body in text/plain

I have an API which accepts both json and text as request body.
#Path("submitObjects")
#Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
#Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
public List<APIResult> submitMultiObjects(#DefaultValue("false") #QueryParam(("suppressWarnings"))Boolean suppressWarnings,List<MyObject> objects) {
// API logic
}
class MyObject{
private String name;
private String description;
// and getters and setters
}
When I hit the API with request body in JSON it works fine.
[{
"name": "name1",
"description": "description1"
},{
"name": "name2",
"description": "description2"
}]
success for above request body
But when I pass request body in plain text it fails with error code 415 - Unsupported media type
name=name1
description=description1
failed for above request body
name=name1
description=description1
name=name2
description=description2
failed for above request body
I wanted to know how we can pass list of objects in plain/ text format.
For below API where I am using MyObject instead of List
#Path("submitObject")
#Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
#Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
public List<APIResult> submitObject(#DefaultValue("false") #QueryParam(("suppressWarnings"))Boolean suppressWarnings,MyObject object) {
// API logic
}
name=name1
description=description1
above request body works fine.

Resources