GraphQL java: Throwing exceptions when request has unused variables - java

Let's say I have a request payload with a variables map and a query. Does graphql-java have any out-of-the-box mechanisms that I can use to detect unused variables in the payload?
For example:
type Query { hello : String! } # this just resolves to "hello world"
When the user sends in {foo: 42} as the variables in the request payload, I want to throw an error (sayIllegalArgumentException).
Edit: Answers/comments on how to implement this without any out-of-the-box mechanisms are also welcome.

Before executing the query , use Parser to parse the query into Document which is an object representation of the query.The variables defined in the query can be accessed by looking its OperationDefinition --> VariableDefinition instance. Then comparing them with the variables in the request payload.
As a bonus ,as the graphql-java engine will parse and validate the query for each query execution and this process can be somewhat time consuming, to avoid parsing and validate it twice, you can use PreparsedDocumentProvider

Related

Difference between #PathVariable, #RequestParam, and #RequestBody

I understand what the #PathVariable, #RequestParam and #RequestBody does in Spring, but not clear on which scenarios we have to use them as they are used for extracting value from the URI. Why we have to send data like localhost:8080/getBooks/time and localhost:8080/getBooks?book=time.
Example 1:
#RequestParam is used mainly for filtering purposes
Lets say you want to get George Martin's book:
GET localhost:8080/books?author=georgemartin
Here we pass author=georgemartin as request parameter. This will supposedly get all of Martin's books, example game of thrones series.
This will be used mainly for GET operation.
Example 2:
#PathVariable is used mainly for getting individual objects or piece of data
Lets say you want to get a book by its id:
GET localhost:8080/books/1
Here we pass 1 as path variable. This will supposedly get the 1 book with id 1, example first part of game of thrones' book.
This will be used mainly for DELETE/GET operation.
Example 3:
#RequestBody is used mainly for saving object(s)(or piece of data)
Lets say you want to add a book:
POST localhost:8080/books/
With request body having following attributes:
{
"author":"George Martin",
"Book":"Game of thrones"
...
...
}
This will add a book to the db. This would be used mainly for PUT/POST operation.
Note: never use verb naming for endpoint, instead, use plural nouns. So books/ is ideal instead of getbooks/.
Reference/Read more:
https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/#h-use-nouns-instead-of-verbs-in-endpoint-paths
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/PathVariable.html
#PathVariable is for parts of the path (i.e. /person/{id})
#RequestParam is for the GET query parameters (i.e. /person?name="Bob").
#RequestBody is for the actual body of a request.
#RequestBody used with POST Verb whereas
#RequestParam and #pathVariable used with GET Verb
#RequstParam : It extract the value from query string
used for fitering,sorting and pagination
In Request Param the values can be encrypted
localhost:8080/getBooks?start=1&end=100
#pathVariable : It extract value from URI Path
In Path variable the value cannot be encoded
Its used get the data based on the value
Reference:
https://www.baeldung.com/spring-requestparam-vs-pathvariable

How to pass list of parameters in rest get call (like filter object in eCommerce )

In my application, there is a requirement of getting data based on some parameters.
I just want to what is the better way to do.
The one way is, I can pass the list of parameters as a path variable.
The second way is, I can pass the request body, I think it is vague and I am not sure it is possible or not.
You can find the code below:
#GetMapping("/cities/{cityName}/latitude/{latitude}/longitude/{longitude}/cityId/{cityId}/street/{street}")
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude() {
}
I just want to know how can I achieve the same.
There is one more question, E-commerce companies have big filter criteria so how they are achieving.
Although there is no hard & fast rule but I generally avoid sending a body in GET request because it's a bad design. You should also refer to this SO Post which contains discussion about using body in GET request. It's an opinionated post and there is no clear YES or NO, but you will get an idea.
HTTP GET with request body
You can either use Path params or query params depending on what those field represent.
Regarding the difference or which to use when I am quoting this answer, which mentions that although there is no hard rule but generally it's better to use params which can uniquely identify the resource as Path param (e.g. id, name etc) and if your param is supposed to do something like filtering/sorting e.g. records after Jan 1 2019 , then go for query param.
Also personally in one of my APIs (which performs filtering), I am using a generic query param, where I pass on JSON object in my query. So basically my API needs to search an object based on variable/multiple attributes. E.g. I have in my db , objects which have certain voltage, current, size etc. values. So, request might come with a combination of 1 or more. So to keep my API flexible, I have provided a query param which can accept JSON object.
So I make a request like this:
{{SERVER}}/api/search?query={voltage:12V,size:10}
And in my API, I can convert this json object to corresponding POJO:
#GET
#Path("/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response search(#QueryParam("query") String queryParam) throws Exception
{
Myobj obj = new Gson().fromJson(queryParam, Myobj.class);
// rest of code
By passing the parameters in the path, you are restricting yourself to extend your API. If you want to extend your API, for example, if you want to filter with criteria as Street1 (or) Street2 then your path wouldnot support it and it will force you to update your API. It is better to pass criteria objects in the body or url parameter. Amazon India is passing criteria like below. I have choosen mobiles with criteria as Manufacturer = Samsung or MI, Storage as 8gb or 4gb and they simply appended the criteria in the query parameters.
There is a third way, Request Params.
#GetMapping
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude(#RequestParam("cityName") String cityName, #RequestParam("latitude") String latitude, #RequestParam("longitude") String longitude){
// Your code
}
For more: 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Parameters using this annotation are required by default, but you can specify that a parameter is optional by setting #RequestParam's required attribute to false (e.g., #RequestParam(value="id", required=false)).
https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#mvc-ann-requestparam

REST: Reference an entity with URI or with UUID?

I'm creating an API that exposes something similar to group memberships. In effect, I have a GroupMembership resource that is exposed at /groupmembership.
Now when I want to create a new association between a user and a group, I will POST to /groupmembership. What I'm curious about is how I should reference the User and Group resource instances. Do I do it through the URI or do I do it through their UUID? That is, which of these two payloads is valid?
POST /groupmembership
{
user: "http://localhost:8080/user/abcd-def-ghij",
group: "http://localhost:8080/group/1a2-b3c-4d5"
}
or
POST /groupmembership
{
user: "abcd-def-ghij",
group: "1a2-b3c-4d5"
}
I am using Spring HATEOAS and as far as I can tell, there is no way to dereference a link to an entity id, which makes the first approach somewhat problematic. Basically, given a link I want to be able to figure out the UUID that references the entity. But I also don't want to parse the URI since they are supposed to be opaque anyway. So can Spring HATEOAS do that?
With the second approach, I can simply look it up, but I wanted to know which approach makes more sense. One thing that bothers me is that the first thing has the distinct flavor of something that should be handled by the client; i.e., it's the client that follows the URI. It seems like the server should simply be able to handle the UUIDs? But on the other hand, the server fully-controls the structure of the URI, and so it seems like it should know how to dereference the URI to the appropriate entity/resource id.
Hi you do not need a payload. Publisch the URL of the created Ressource through the Location header with status code 201.
To answer your question, use the URI.
The client shouldn't compose URIs on his own to get the resources.

How do I debug what's wrong when the Endpoints framework stops generating the WEB-INF/*.api-file?

Given a Google Cloud Endpoints project in Eclipse with the servlet-class annotated with #Api(name="helloworld"), the Endpoints framework generates a file named war/WEB-INF/helloworld-v1.api when the project compiles successfully. Sometimes this file is not generated even if there are no compilation errors though - only what I will call "GAE Endpoints code convention errors".
Example - working:
public class TestEntity {
public String Text;
public TestEntity(String text){
Text = text;
}
}
#ApiMethod
public TestEntity getTestEntity(){
return new TestEntity("Hello world");
}
Example - NOT working:
// The TestEntity-class is unchanged
#ApiMethod
public TestEntity getTestEntity(String input){
return new TestEntity("Hello world");
}
The problem with the latter example is that I take a String parameter as input without annotating it with #Named. I know that in this example, but there might be other cases where this is not so obvious.
Is there anywhere where I can read some sort of error log on why the .api file is not generated?
Although I am a fan of code by convention, it really takes the programming efficiency a step back if I cannot get feedback on what I do wrong. Eclipse provides compiler error feedback. The Google Cloud Endpoints Framework should provide Code-By-Convention-Rule-Breaking feedback.
There isn't currently good logging or error messaging when code generation fails, though it's one of the (if not most) requested features. In the interim, here's a list of the common failure cases:
The return type is invalid. Return types must be objects conforming to JavaBean conventions, and types like Object, String, and Integer are not allowed.
One or more argument types are invalid. Methods may accept at most one object in the POST body, and this object should also conform to JavaBean conventions. Methods may accept zero or more arguments via the query string (using the #Named annotation) and these must be scalar types (e.g. String, Integer).
An API, method, or parameter has an invalid name. APIs, methods, and parameters should be named to match the following regular expression: [a-z]+[A-Za-z0-9]*. Convention also suggests using lowerCamelCase for naming (though alllowercase is allowed).

renderJson without template

I have controller with
render(messages);
And i have route
GET / Application.index
I want to implement some rest features, and add this route
GET /api/index Application.index(format:'json')
I have template not found exception. How can i say to play use renderJSON() when format is json without any code changes?
As you use the render() method, Play! will search a template file with the name of the action (detail on Play! website : http://www.playframework.org/documentation/1.2.2/controllers#template).
You have to use renderJSON(params...), it will bypass the default template!
Your use case doesn't really make sense. In the standard render() call, you are likely passing some pojos to the template to use (or none at all), this is a varargs method. In the renderJSON() call you always need to pass an object which can be serialized to json by the Gson library, or a string with is already in json.
Add a new method to your Application class that handles json responses:
# normal index page
GET / Application.index
# api request
GET /api/index Application.indexJson

Categories