I have to send a query with some headers and graphQL variables as a POST call to a GraphQL API in java. I also have to send some headers and authentication parameters in the query. Right now, I am doing a manual call in POSTMAN, but I want to do this programmatically. Can you guys help me where to start off. My query and variables are as follows
query sampleQuery($param: SampleQueryParams!, $pagingParam: PagingParams) {
sampleLookup(params: $param, pagingParams: $pagingParam) {
ID
name1
name2
}
}
And my GraphQL variables are as follows :
{"param": {"id": 58763897}, "pagingParam": {"pageNumber": 0, "pageSize": 10 } }
I have no clue where to start at. Could you guys please help
Below is a typical graphql endpoint for a java backend
There are 2 basic flows here
1 an endpoint for http request that can handle the graghql query as a string and a map / json representation of the query's input variables
2 the graphql wiring for the backend that collates and returns the data
the backend would typicaly have an endpoint that looks like this (1)
public Map<String, Object> graphqlGET(#RequestParam("query") String query,
#RequestParam(value = "operationName", required = false) String operationName,
#RequestParam("variables") String variablesJson) throws IOException {...
note we have 3 inputs
a query string,
a string usually json for the querys variables
an optional "operationName"
once we have parsed these input parameters we would typically send them to the graphql implementation for the query
which could look like this (1)
private Map<String, Object> executeGraphqlQuery(String operationName,
String query, Map<String, Object> variables) {
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.query(query)
.variables(variables)
.operationName(operationName)
.build();
return graphql.execute(executionInput).toSpecification();
}
here the graphql object has all the wiring to return the data
So a solution is just to post the correctly formatted input parameters to the backend
I often use android and a http client that works with older android versions so a post request in kotlin could look like this as a very simple example
val client = HttpClients.createDefault()
val httpPost = HttpPost(url)
val postParameters = ArrayList<NameValuePair>()
postParameters.add(BasicNameValuePair("query", "query as string"))
postParameters.add(BasicNameValuePair("variables", "variables json string"))
httpPost.entity = UrlEncodedFormEntity(postParameters, Charset.defaultCharset())
val response = client.execute(httpPost)
val ret = EntityUtils.toString(response.getEntity())
please note the implementation for the http post with be dependent on the way the backend java implementation is set up
for the basic http client and post setup many good examples here
How to use parameters with HttpPost
possibly related
graphql allows for a introspection flow which publishes details on the query structure the implementation supports
more info here
https://graphql.org/learn/introspection/
[1] https://github.com/graphql-java/graphql-java-examples
I would recommend using graphql-java-codegen plugin for these purposes.
It provides a possibility to generate classes based on the schema which you can supply to any HTTP client.
For example, GraphQL server has following schema and we want to perform productById query:
type Query {
productById(id: ID!): Product
}
type Product {
id: ID!
title: String!
price: BigDecimal!
}
graphql-java-codegen will generate all classes required for you to perform a query:
// preparing request
ProductByIdQueryRequest request = new ProductByIdQueryRequest();
request.setId(productId);
// preparing response projection (which fields to expect in the response)
ProductResponseProjection responseProjection = new ProductResponseProjection()
.id()
.title()
.price();
// preparing a composite graphql request
GraphQLRequest graphQLRequest = new GraphQLRequest(request, responseProjection);
// performing a request with the constructed object
ProductByIdQueryResponse responseBody = restTemplate.exchange(URI.create("https://product-service:8080/graphql"),
HttpMethod.POST,
new HttpEntity<>(graphQLRequest.toHttpJsonBody()),
ProductByIdQueryResponse.class).getBody();
// Fetching a serialized object from response
Product product = responseBody.productById();
More examples can be found on GitHub: https://github.com/kobylynskyi/graphql-java-codegen#supported-plugins
Related
I am having a hard time finding documentation on which is the correct return type.
For example, if I have a REST endpoint which lookups and returns a String should the endpoint have a return type of Future<String> or String? Further, what impact would this have on the event loop (i.e. would returning String over Future<String> cause more blocking)?
Thanks!
If you look at section (2) of the quick-start at https://vertx.io/get-started, you'll see chunk of code I've pasted below (I've added some numbered comments):
// Mount the handler for all incoming requests at every path and HTTP method
router
.route() // (1)
.handler(context -> { // (2)
// Get the address of the request
String address = context.request().connection().remoteAddress().toString();
// Get the query parameter "name"
MultiMap queryParams = context.queryParams();
String name = queryParams.contains("name") ? queryParams.get("name") : "unknown";
// Write a json response
context.json( // (3)
new JsonObject()
.put("name", name)
.put("address", address)
.put("message", "Hello " + name + " connected from " + address)
);
});
What this is doing is:
Registering a Handler (basically a callback) that will be invoked for every request that the router receives.
The handler will be called with a RoutingContext, which contains references to an HttpServerRequest object representing the current request, as well as an HttpServerResponse object representing the response. The latter allows you to control the response that is sent back to the client (ie headers, body, etc).
context.json() is a convenience method for writing a JSON formatted response payload - the body will be correctly formatted, the content-type header will be set, etc.
Fundamentally, what .json() is doing is:
final JsonObject myJson = ...;
final Buffer myJsonBuffer = Json.encodeToBuffer(myJson);
context.response()
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.write(myJsonBuffer);
Those last three lines are how the response is actually sent back to the client.
For a more detailed explanation, check out the Vert.x Web documentation regarding responses here.
Out of the box Vert.x allows any primitive/simple type, String, or
buffers to be sent as messages. However, it’s a convention and common
practice in Vert.x to send messages as JSON
I guess this is the documentation you are looking for. You can return Strings to the eventBus. Though Json is mostly used
new JsonObject().put("key", "stringValue");
It's better to return the String than the Future. The Future will need special Codec.
I have a RESTful service controller that requests another RESTful service
#ResponseBody
#RequestMapping(value = "/headerparameters/{instanceId}", method = RequestMethod.DELETE)
public RestContainerFormBean passivizeHeaderParameter(#PathVariable String instanceId) throws GenericException, IOException {
String url = proactiveURL + "/customerheaders/" + instanceId;
if(isSecurityCheckOK(url)){
ResponseEntity<CustomerHeaderParameterBean> response = restTemplate.exchange(url, HttpMethod.DELETE, new HttpEntity<>(new HttpHeaders()), CustomerHeaderParameterBean.class);
CustomerHeaderParameterBean result = response.getBody();
setButtonActivity(result);
l10nOfValue(result);
return new RestContainerFormBean(result);
} else{
throw new IOException();
}
}
This code can not pass SonarQube policy.
Refactor this code to not construct the URL from tainted,
User provided data, such as URL parameters, POST data payloads, or
cookies, should always be considered untrusted and tainted. A remote
server making requests to URLs based on tainted data could enable
attackers to make arbitrary requests to the internal network or to the
local file system.
The problem could be mitigated in any of the following ways:
Validate the user provided data based on a whitelist and reject input
not matching. Redesign the application to not send requests based on
user provided data.
How can I pass the policy by sticking on REST conventions ?
Use UriComponentsBuilder to encode the URL instead of using raw URL.
I'm trying to create a Java program, that will allow users to record and store API calls and responses in a NoSQL database. Basically, I want to take in the call name, the parameters, and the response and store that in the database. The intention here is to let users re-use the calls w/ the same parameters by accessing the data from the database, as opposed to making a live API call. Is there a way to listen to API calls on a specific URL using Java, and if so how can it be done?
I'm not sure if I completely understand the reason for doing it. But the answer to this question is tightly dependent on the technology you want to use for HTTP client, type of request/response and the NoSQL database and strategy you want to use.
As a suggestion, you can create two data classes, RequestDummy and ResponseDummy with all the required fields you need and use something like gson to serialize or deserialize them and store them as JSON file with the database to be more readable. Ex:
class RequestDummy {
String url;
String method; // (GET|HEAD|POST|PUT|PATCH|DELETE)
Map<String, String> headers;
String payload; // For (POST|PUT|PATCH) use Base64 to encode the binary to String
}
class ResponseDummy {
Map<String, String> headers;
int statusCode;
String body; // use Base64 to encode the binary to String
}
And use them during the HTTP call (briefly to just explain the idea):
// Initialize RequestDummy
RequestDummy req ...
HttpURLConnection con = (HttpURLConnection) new URL(req.url).openConnection();
// optional default is GET
con.setRequestMethod(req.method);
req.headers.entrySet().forEach(e -> con.setRequestProperty(e.getKey(), e.getValue()));
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader());
byte[] data = con.getInputStream().readAllBytes() // Java9
// Initialize ResponseDummy
RequestDummy req ...
in.close();
In 99% storing request and response is a bad design. As a suggestion, it's not a good idea to store HTTP request and response for any usage. They are just for communication, you must store the requested data which the HTTP request built based on, and the generated data from the HTTP response.
I want to write a JUnit class for a REST endpoint.
This is my REST method. It works fine.
#POST
#Path("create")
#Produces(APPLICATION_JSON)
public String create(#QueryParam("parentId") String parentId, #QueryParam("name") String name) {
//do sth.
return "{\"status\": \"SUCCESS\"}";
}
Now my JUnit test looks like that, which doesn't work, because I don't know how to POST my data in the right way:
#Test
public void testCreate() {
Client client = ClientBuilder.newClient();
WebTarget wt = client.target(REST_MENU_URL + "create");
String queryParams = "parentId=1&name=NEW_JUnit_NEW";
// In the line below, I want to POST my query parameters, but I do it wrong
Response response = wt.request().post(Entity.entity(queryParams, APPLICATION_JSON), Response.class);
// The response has a 500, because the query parameters are all NULL!
assertEquals("Http code should be 200", 200, response.getStatus());
}
So how do I have to change the line with the 'Response' to make it work?
The problem is, that the query parameters (parentId and name) don't get transmitted (response = wt.request().post(...)).
I tried to POST form parameters too, but no success here either. Just like that:
Form form =new Form().param("parentId", "4").param("name", "NEW_JUnit_NEW");
Response response = wt.request().post(Entity.entity(form, APPLICATION_JSON), Response.class);
Thanks,
Bernhard
Check out the Jersey Client documentation, in particular section 5.3.4 on targeting resources.
Query parameters form a part of the URI of the resource, they're not part of the body of the document posted to the resource. You're seeing null in your resource because you're not filling in the query parameters in the URI, you're posting them as the body. You need to tell Jersey to put them in the URI...
WebTarget wt = client.target(REST_MENU_URL + "create").queryParam("parentId", 1).queryParam("name", "NEW_JUnit_NEW");
You'll also need to ensure that your POST request sets the Accept header to allow application/json (by calling the accept(...) method after calling request()) and you're going to need to construct some kind of Entity to pass to the post(...) method - the problem here is that your resource is not consuming the entity body but the client API expects you to send something - this is a code smell which suggests your API is not particularly ReSTful. You can probably get away with some kind of empty body constructed from an empty string. It should look a bit like this...
Response response = wt.request().accept(MediaType.APPLICATION_JSON).post(Entity.text(""))
Alternatively, you could look into converting your API so that it accepts a JSON document and move the query parameters into that document.
I'm trying to implement a server-side API method that allows a batch of API requests to be executed as part of a single request, with the response for each request in the batch wrapped into a JSONArray that is returned to the client.
In essence, the client calls the server with a "batch" parameter along the lines of:
[{method: "getStatus" userId: "5"}, {method: "addFriend", userId: "5", friendId: "7"}]
This specifies a batch composed to two API calls. What I want to do is execute each one, and combine the responses into something like:
[{status: "success", status: "At work..."}, {status: "error", message: "Friend not found!"}]
To execute the batch, I am iteratively calling RequestDispatcher.include(), as such:
String format = request.getParamter("format"); //carry the requested response format forward for each batched request
JSONArray batchResponse = new JSONArray();
RequestDispatcher dispatcher = request.getRequestDispatcher("/apiContext");
OverridableHttpRequest reusableRequest = new OverridableHttpRequest(request);
JSONArray requests = (JSONArray)JSONValue.parse(request.getParameter("batch"));
for (Object batchedRequest : requests) {
reusableRequest.reset(); //clear all params and attribs
//set the parameters to use for this request
JSONObject requestParams = (JSONObject)batchedRequest;
for (Object key : requestParams.keySet()) {
reusableRequest.setParameter(key.toString(), requestParams.get(key).toString());
}
reusableRequest.setParameter("format", format);
LOG.debug("including: /apiContext?" + reusableRequest.getQueryString());
//process the request as if it were received normally
dispatcher.include(reusableRequest, response); //FIXME: how to get the response data for this include into 'batchResponse'?
}
Everything works well (all the batched requests are executed, and the server processes them correctly), but I can't figure out how to get the included response out so that I can add it to the results array.
Any ideas?
I would first try to avoid going through the servlet stack when handling the individual requests. Cannot you just call some of your business methods directly? I appreciate that you want to re-use the dispatching and parameter parsing logic, but maybe that part is not very complicated.
If that is not possible, maybe you can add a request.setAttribute("theResult", jsonData) into the individual handlers, so that you do not have to look at the text result, but can retrieve the data more easily.
If you still want to look at the response stream, you need to create a ResponseWrapper. For example check out this question.