How can I put a list as a variable in GraphQL tester in Spring? - 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.

Related

Webflux subscribe to nested Publishers and serialize them to JSON

I have a UserDto with related items taken from repository which is Project-Reactor based, thus returns Flux/Mono publishers.
My idea was to add fields/getters to DTO, which themselves are publishers and lazily evaluate them (subscribe) on demand, but there is a problem:
Controller returns Flux of DTOs, all is fine, except spring doesn't serialize inner Publishers
What I'm trying to achieve in short:
#Repository
class RelatedItemsRepo {
static Flux<Integer> findAll() {
// simulates Flux of User related data (e.g. Orders or Articles)
return Flux.just(1, 2, 3);
}
}
#Component
class UserDto {
// Trying to get related items as field
Flux<Integer> relatedItemsAsField = RelatedItemsRepo.findAll();
// And as a getter
#JsonProperty("related_items_as_method")
Flux<Integer> relatedItemsAsMethod() {
return RelatedItemsRepo.findAll();
}
// Here was suggestion to collect flux to list and return Mono
// but unfortunately it doesn't make the trick
#JsonProperty("related_items_collected_to_list")
Mono<List<Integer>> relatedItemsAsList() {
return RelatedItemsRepo.findAll().collectList();
}
// .. another user data
}
#RestController
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController {
#GetMapping
Flux<UserDto> dtoFlux() {
return Flux.just(new UserDto(), new UserDto(), new UserDto());
}
}
And this is the response I get:
HTTP/1.1 200 OK
Content-Type: application/json
transfer-encoding: chunked
[
{
"related_items_as_method": {
"prefetch": -1,
"scanAvailable": true
},
"related_items_collected_to_list": {
"scanAvailable": true
}
},
{
"related_items_as_method": {
"prefetch": -1,
"scanAvailable": true
},
"related_items_collected_to_list": {
"scanAvailable": true
}
},
{
"related_items_as_method": {
"prefetch": -1,
"scanAvailable": true
},
"related_items_collected_to_list": {
"scanAvailable": true
}
}
]
It seems like Jackson doesn't serialize Flux properly and just calls .toString() on it (or something similar).
My question is: Is there existing Jackson serializers for Reactor Publishers or should I implement my own, or maybe am I doing something conceptually wrong.
So in short: how can I push Spring to evaluate those fields (subscribe to them)
If I understand correctly, what you try to achieve is to create an API that needs to respond with the following response:
HTTP 200
[
{
"relatedItemsAsField": [1,2,3]
},
{
"relatedItemsAsField": [1,2,3]
},
{
"relatedItemsAsField": [1,2,3]
}
]
I would collect all the elements emitted by the Flux generated by RelatedItemsRepo#findAll by using Flux#collectList, then map this to set the UserDto object as required.
Here is a gist.

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:

How to pass file in my GraphQL query for my Integration test?

So I'm trying to write an Integration test for my query that accepts a file (Upload scalar) from Apollo.
#Test
void imageFromSimilarImage() throws IOException {
when(imageService.findBySimilarImage(/*** some-file ***/)).thenReturn(TEST_IMAGE_LIST);
GraphQLResponse response = graphQLTestTemplate.postForResource("graphql/image-from-similar-image.graphql");
assertThat(response.isOk()).isTrue();
assertThat(response.getList("$.data.imageFromSimilarImage", Image.class)).contains(TEST_IMAGE_A);
}
my image-from-similar-image.graphql file:
query {
imageFromSimilarImage(file: /*** "some-file-content" ***/) {
url
cloudinaryId
tags {
value
}
}
}
My original graphql schema, image.graphql file, if needed...
scalar Upload
schema {
query: Query
mutation: Mutation
}
type Image {
id: ID!
cloudinaryId: String!
url: String!
tags: [Tag]
}
type Tag {
id: ID!
value: String!
image: Image!
}
type Query {
imageFromTag(tags : [String!]!): [Image]
imageFromSimilarImage(file: Upload!): [Image]
allImages : [Image]
}
type Mutation {
createImage(files: [Upload!]!) : [Image]
}
Any ideas? I've browsed around for this , however couldn't find anything, with Junit ...
I solved this by changing the signature of my query imageFromSimilarImage to the following:
imageFromSimilarImage(files: [Upload!]!) : [Image]
Basically what this allowed me to do, is to pass an empty array in my test graphql file,
query {
imageFromSimilarImage(file: []) {
url
cloudinaryId
tags {
value
}
}
}
and finally in my test case:
#Test
void imageFromSimilarImage() throws IOException {
when(imageService.findBySimilarImage(anyList())).thenReturn(TEST_IMAGE_LIST);
GraphQLResponse response = graphQLTestTemplate.postForResource("graphql/image-from-similar-image.graphql");
assertThat(response.isOk()).isTrue();
assertThat(response.getList("$.data.imageFromSimilarImage", Image.class)).contains(TEST_IMAGE_A);
}
However, this does change the behavior of the query, as in it accepts Multiple files, now instead of just one, although in my case this does not affect anything.
Just keep this in mind, and make sure it is acceptable in your case as well.

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.

Spring ModelAndView to RestController json response

I have a legacy spring code where they use ModelAndView and they add the objects to it as below.
ModelAndView result = new ModelAndView();
result.addObject("folders", folders);
return result;
for the above i am getting response as
{
"folders": [
{
"recordCount": 0,
"folderContentType": "Reports",
"folderId": 34,
},
{
"recordCount": 2,
"folderContentType": "SharedReports",
"folderId": 88,
}
]
}
I have changed these to use Spring's RestController with a POJO backing the results returned from DB.
#GetMapping("/folders")
public List<Folder> getAllFolders() {
return Service.findAllFolders(1,2);
}
This returns a JSON as below
[
{
"folderId": 359056,
"folderName": "BE Shared Report Inbox",
"folderDescription": "BE Shared Report Inbox",
},
{
"folderId": 359057,
"folderName": "BE Shared Spec Inbox",
}]
How could i return this as exactly as my legacy code response. I know i can convert the List to Map and display. But, is there any equivalent
way.
Thanks.
You can put your result into a map.
#GetMapping("/folders")
public List<Folder> getAllFolders() {
return Service.findAllFolders(1,2);
}
Change to:
#GetMapping("/folders")
public Map<String,List<Folder>> getAllFolders() {
Map<String,List<Folder>> map = new HashMap<>();
map.put("folders",Service.findAllFolders(1,2));
return map;
}

Resources