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

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:

Related

How can I put a list as a variable in GraphQL tester in Spring?

If there is an array inside the DTO, I tried using List<Map<String, Object>> but it doesn't work properly. Could you possibly know how?
Below is my code.
Below is my code.
#Test
void graphqlTest() {
Map<String, Object> tag = new ConcurrentHashMap<>() {{
put("id", 1L);
put("name", "aaaaa");
}};
List<Map<String, Object>> tagInputList = new ArrayList<>();
tagInputList.add(tag);
Map<String, Object> input = new ConcurrentHashMap<>() {{
put("list", tagInputList);
}};
graphQlTester.documentName("tag")
.operationName("followMyTags")
.variable("input", input)
.execute()
.errors()
.verify()
.path("followMyTags.list")
.entityList(TagDto.class);
}
Below is error message.
Response has 1 unexpected error(s) of 1 total. If expected, please filter them out: [server operation something wrong]
Request: document='query queryTags {
queryTags {
list {
id
name
}
}
}
query queryFollowedTags {
queryFollowedTags {
list {
id
name
}
}
}
mutation followMyTags($input: TagsInput!) {
followMyTags(input: $input) {
list {
id
name
}
}
}
mutation unfollowMyTags($input: TagsInput!) {
unfollowMyTags(input: $input) {
list {
id
name
}
}
}', operationName='followMyTags', variables={input={list=[{name=aaaaa, id=1}]}}
java.lang.AssertionError: Response has 1 unexpected error(s) of 1 total. If expected, please filter them out: [server operation something wrong]
I tried using an array instead of a list via toArray, but it still didn't work.

Is there any way to create custom aws lambda authorizer using Spring Boot Function?

I am trying to create a custom API Gateway lambda authorizer using Spring Cloud Function.
But when I am trying to return policy document as Map<String,Object> from Spring Function, Spring returning it as body along with some extra metadata. So ApiGateway treating it as invalid json. How to return only policy document.
This is what the String Function returning when I return Map<String,Object>. My Map Object data is in body.
{
"isBase64Encoded":false,
"headers":{
"id":"6b9da9d5-23a7-b555-aecf-90e6134779b8",
"contentType":"application/json",
"timestamp":"1630300611676"
},
"body":"{\"policyDocument\":{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"execute-api:Invoke\",\"Resource\":\"arn-value\",\"Effect\":\"Allow\"}]},\"context\":{\"name\":\"test\"},\"principalId\":\"813bf219-6039-4065-bcce-3e03b6996872\"}",
"statusCode":200
}
But it should actually return only body part for ApiGateway to work correctly.
{
"policyDocument":{
"Version":"2012-10-17",
"Statement":[
{
"Action":"execute-api:Invoke",
"Resource":"arn-value",
"Effect":"Allow"
}
]
},
"context":{
"name":"test"
},
"principalId":"813bf219-6039-4065-bcce-3e03b6996872"
}
My Spring Cloud Function code is below. It is only test function without proper logic.
#Bean
public Function<APIGatewayProxyRequestEvent, Map<String,Object>> authorise() {
return request -> {
String token = request.getHeaders().get("Authorization");
String arn = String.format("arn:aws:execute-api:%s:%s:%s/%s/%s/",
System.getenv("AWS_REGION"),
request.getRequestContext().getAccountId(),
request.getRequestContext().getApiId(),
request.getRequestContext().getStage(),
request.getRequestContext().getHttpMethod());
// Policy policy = jwtToken.getPolicy(token, arn);
try{
String allow = request.getHttpMethod().equalsIgnoreCase("GET")?"Allow":"Deny";
JSONObject json = new JSONObject();
json.put("principalId", UUID.randomUUID().toString())
.put("policyDocument",
new JSONObject()
.put("Version", "2012-10-17")
.put("Statement",
new JSONArray()
.put(new JSONObject()
.put("Action", "execute-api:Invoke")
.put("Effect", allow)
.put("Resource", arn)
)
)
)
.put("context", new JSONObject().put("name", "test"));
return json.toMap();
}catch(Exception e){
logger.error("",e);
}
return null;
};
}

Spring rest api same url for bulk and single request

I would like to have this API in a Spring boot app:
POST /items
{
"name": "item1"
}
POST /items
[
{
"name": "item1"
},
{
"name": "item2"
},
]
So the same endpoint could accept array or a single element in a json body.
Unfortunatelly this doesn't work:
#PostMapping(path="items")
public ResponseEntity<String> items(#RequestBody Item item) {}
#PostMapping(path="items")
public ResponseEntity<String> items(#RequestBody List<Item> items) {}
I also tried this:
#PostMapping(path="items")
public ResponseEntity<String> items(#RequestBody #JsonFormat(with= JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) List<Item> items) {}
it doesn't work.
If I wrap the list like:
public class Items {
#JsonFormat(with= JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<item> items;
}
then unfortunately my request body would look like:
{
"items": [
{
"name": "item1"
},
{
"name": "item2"
},
]
}
Do you know how I can have such API with spring boot?
You want a variable that can directly hold an Array or an Object. There is no straightforward way to achieve something like that because of the Static Typing restrictions of Java.
The only way I can imagine so far to achieve something like this is to create a single API that takes some generic type.
Like an Object:
#PostMapping(path="items")
public ResponseEntity<String> items(#RequestBody Object body) {
if(body instanceof List) {
} else {
}
}
Or a String:
#PostMapping(path="items")
public ResponseEntity<String> items(#RequestBody String body) {
if(body.charAt(0) == '[' && body.charAt(body.length() - 1) == ']') {
} else if(body.charAt(0) == '{' && body.charAt(body.length() - 1) == '}') {
} else {
}
}
You'll have to do a lot of work manually.
Also, you can't create two APIs with the same path /items and the same method POST, they will raise a compile-time error.

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

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?

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.

Resources