americanexpress / nodes Goto Github PK
View Code? Open in Web Editor NEWA GraphQL JVM Client - Java, Kotlin, Scala, etc.
License: Apache License 2.0
A GraphQL JVM Client - Java, Kotlin, Scala, etc.
License: Apache License 2.0
According to the graph ql spec , the error object follows the following format .
https://facebook.github.io/graphql/June2018/#sec-Errors
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ],
"extensions": {
"code": "CAN_NOT_FETCH_BY_ID",
"timestamp": "Fri Feb 9 14:33:09 UTC 2018"
}
}
]
}
Requirement:
I am attempting to capture the branches of a GitHub repository using the GitHub GraphQL API v4. The following is an example GraphQL query for capturing the branches of repository wso2/docker-ei
.
{
organization(login: "wso2") {
name
url
repository(name: "docker-ei") {
refs(first: 10, refPrefix: "refs/heads/") {
edges {
node {
name
}
}
}
}
}
}
Current Status:
For this purpose, we need to capture the Git References (refs
). I have managed to build up the Java implementation for building the following query, to capture the Git References (en route to creating the final query).
{
organization(login: "wso2") {
name
repository(name: "docker-ei") {
issue(number: 32) {
title
}
refs(first: 6, refPrefix: "refs/heads/") {
totalCount
}
name
}
url
}
}
I have created three relevant Java classes (DTOs) Organization.java, Repository.java and Refs.java in order to build up the above query. The content of these classes are as follows:
Organization.java
@GraphQLProperty(name = "organization",
arguments = {@GraphQLArgument(name = "login", type = "String")}
)
public class Organization {
private String name;
private String url;
@GraphQLArguments({
@GraphQLArgument(name = "name", type = "String")
})
private Repository repository;
public String getName() {
return name;
}
public String getUrl() {
return url;
}
public Repository getRepository() {
return repository;
}
}
Repository.java
@GraphQLProperty(name = "repository")
public class Repository {
private String name;
@GraphQLArguments({
@GraphQLArgument(name = "number", type = "Int")
})
private Issue issue;
@GraphQLArguments({
@GraphQLArgument(name = "first", type = "Int"),
@GraphQLArgument(name = "refPrefix", type = "String")
})
private Refs refs;
public String getName() {
return name;
}
public Issue getIssue() {
return issue;
}
public Refs getReferences() {
return refs;
}
}
Refs.java
@GraphQLProperty(name = "refs")
public class Refs {
private int totalCount;
public int getTotalCount() {
return totalCount;
}
}
The following code snippet depicts how I am executing the query:
GraphQLRequestEntity requestEntity = null;
try {
requestEntity = GraphQLRequestEntity.Builder()
.headers(headers)
.url("https://api.github.com/graphql")
.request(Organization.class)
.arguments(
new Arguments("organization", new Argument("login", "wso2")),
new Arguments("organization.repository", new Argument("name", "docker-ei")),
new Arguments("organization.repository.issue", new Argument("number", 32)),
new Arguments("organization.repository.refs", new Argument("first", 6), new Argument("refPrefix", "refs/heads/"))
)
.build();
} catch (MalformedURLException e) {
e.printStackTrace();
}
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
GraphQLResponseEntity<Organization> responseEntity = graphQLTemplate.query(requestEntity, Organization.class);
Issue:
But I am experiencing the following exception when executing the query (although refs
is a valid field under repository
):
Exception in thread "main" GraphQLException{message='OK', status='200', description='Unrecognized field "refs" (class org.wso2.carbon.wum.git.Repository), not marked as ignorable (2 known properties: "issue", "name"])
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: io.aexp.nodes.graphql.Wrapper["data"]->org.wso2.carbon.wum.git.Organization["repository"]->org.wso2.carbon.wum.git.Repository["refs"])', errors=null}
at io.aexp.nodes.graphql.Fetch.send(Fetch.java:84)
at io.aexp.nodes.graphql.GraphQLTemplate.execute(GraphQLTemplate.java:83)
at io.aexp.nodes.graphql.GraphQLTemplate.query(GraphQLTemplate.java:42)
at org.wso2.carbon.wum.git.GraphQLRequestSender.main(GraphQLRequestSender.java:44)
During debugging it was identified that the GraphQL query is accurately created and when executed using the GitHub GraphQL v4 explorer, the desired results can be obtained.
Any help or suggestions in relation with fixing this issue, are highly appreciated.
Jacoco stops command-line Maven builds since Parameter test coverage is less than configured threshold (90% coverage). The Nodes package cannot be installed in a local Maven repository.
Currently there doesn't appear to be a way to support inline fragments, I am looking to do something like:
query {
someInterface {
... on SomeType {
field
}
}
}
I may have missed how to do this, I tried decorating a field with @GraphQLProperty
but it gave me back something that looked far more like a field alias than an Inline Fragment.
Thoughts?
If this is missing and there is consensus on how to do this, I'd be happy to put together a PR!
Hi! Thank you for developing this client!
Is there any way to use nodes with recursive relationships? For instance, is it possible to generate the following query
{
query {
cars(id: 1) {
id
owner {
cars {
id
}
}
}
}
}
to list all cars owned by the owner of this car?
Currently the query-generation results in a stack overflow
To keep in parity with the GraphQL specification it would be great to add support for subscriptions
Enhancement: consider supporting HttpsURLConnection
javax.net.ssl.HttpsURLConnection
in order to request secure https API endpoints.
Seems like there are only two ways to build a request, one thru a predefined class and the other thru a raw query string.
Could you add support for java.util.Map
requests?
Such as:
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
Map<String, Property> request = new HashMap<>();
request.put("someField", Property.Builder().arguments(..).build());
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("http://graphql.example.com/graphql");
.variables(new Variable("timeFormat", "MM/dd/yyyy"))
.arguments(new Arguments("path.to.argument.property",
new Argument("id", "d070633a9f9")))
.request(request)
.build();
GraphQLResponseEntity<Map> responseEntity = graphQLTemplate.query(requestEntity, Map.class);
I was testing out this library via the Spotify test API : https://spotify-graphql-server.herokuapp.com/graphql
The query would be
query ($byName: String) {
queryArtists(byName: $byName) {
image
albums {
image
name
id
tracks {
preview_url
name
id
}
}
name
id
}
}
with variables byName : "Red Hot Chili Peppers"
Debugging shows it sending the right query and data back but it fails to Deserliase into a structure
Exception in thread "main" GraphQLException{message='OK', status='200', description='Unrecognized field "queryArtists" (class com.atlassian.model.Query), not marked as ignorable (0 known properties: ])
at [Source: (String)"{"queryArtists":[{"image":"https://i.scdn.co/image/5b2072e522bf3324019a8c2dc3db20116dff0b87","albums":[{"image":"https://i.scdn.co/image/8deed63f89e0a215e90de1ee5809780921a47747","name":"The Getaway","id":"43otFXrY0bgaq5fB3GrZj6"},{"image":"https://i.scdn.co/image/66ab66e38e40a9202e8417a0b59ad86a210637a7","name":"I'm With You","id":"5wZtSIvijWCMc1vlPFqAyB"},{"image":"https://i.scdn.co/image/fd62b1f4f697284a024784706949bff3a6e1a27e","name":"Stadium Arcadium","id":"7xl50xr9NDkd3i2kBbzsNZ"},{"image"[truncated 3654 chars]; line: 1, column: 18] (through reference chain: io.aexp.nodes.graphql.Wrapper["data"]->com.atlassian.model.Query["queryArtists"])', errors=null}
at io.aexp.nodes.graphql.Fetch.send(Fetch.java:96)
at io.aexp.nodes.graphql.GraphQLTemplate.execute(GraphQLTemplate.java:99)
at io.aexp.nodes.graphql.GraphQLTemplate.query(GraphQLTemplate.java:58)
at com.atlassian.Main.main(Main.java:23)
My code is this
public static void main(String[] args) throws MalformedURLException {
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("https://spotify-graphql-server.herokuapp.com/graphql")
.variables(new Variable("byName", "Red Hot Chili Peppers"))
.scalars(BigDecimal.class)
.request(Query.class)
.build();
GraphQLResponseEntity<Query> responseEntity = graphQLTemplate.query(requestEntity, Query.class);
System.out.println(responseEntity);
}
My model classes are
public class Query {
@GraphQLVariable(name = "byName", scalar = "String")
List<Artist> queryArtists;
}
public class Artist {
String name;
String id;
String image;
List<Album> albums;
}
public class Album {
String name;
String id;
String image;
//List<Track> tracks;
}
public class Track {
String name;
//List<Artist> artists;
String preview_url;
String id;
}
I guess I am unsure about where the entry point for queries are. The samples in the library only every enter a single value field (eg User via github /user) and not a list field like above
Am I off track on how this gets set up?
Not familiar with the Java eco system, it was non obvious to me to try jcenter to find the dependency. It should probably be mentioned in the read me and included in the maven install examples.
Maven
<repositories>
<repository>
<id>jcenter - amex</id>
<url>https://dl.bintray.com/americanexpress/maven/</url>
</repository>
</repositories>
io.aexp.nodes.graphql.Fetch uses a Java InputStreamReader without explicit charset. If Nodes is running under a JVM with default encoding other than UTF-8 (i.e. under Windows), the response deserialization will not handle the JSON strings in the UTF-8 server response correctly since the deserialization is based on the stream reader with JVM default charset (!= UTF-8 i.e. different from actual response). Thus any string values with extended characters will contain invalid/mis-encoded values. E.g. "Martin Kalén" will be deserialized as "Martin Kalén".
query {
myUser: user(username: "visa") { ... }
myFavoriteUser: user(username: "amex") { ... }
}
If i were to have a query like the one above, how is it possible to implement it with Nodes?
example
xyz(abc:[{def:enum, value:"string"},{def:enum, value:"string"}]) {
}
Hey everyone,
sorry, but I couldn't find another way of contacting you than opening an issue.
Is there any plan for releasing a next version soon? Especially, I'm looking at the changes from PR #75 which I see as basically mandatory for any HTTP connection.
Cheers,
Ronny
Unlike GraphQLProperty, GraphQLVariable(s) isn't class level -- I'm not sure how I'm suppose to use them to achieve my query/mutation.
I'm trying to build the following mutation
mutation Create2FA($inputDescriptor: Input2FA!) {
Create2FA(inputDescriptor: $inputDescriptor) {
TwoFAID
Description
...
}
}
Or A more general usage example for Queries:
query FindProducts($filter: InputFilter!) {
FindProductsWithFilter(filter: $filter) {
SKU
Description
}
}
I was anticipating a model definition like (but illegal to put variables @ class level):
@GraphQLProperty(name = "Create2FA")
@GraphQLVariables({
@GraphQLVariable(name = "TwoFA", scalar = "CreateTwoFA!")
})
public class MutationCreate2FA extends TwoFAModel {
}
These are my (legal) options but all provide the wrong result.
Test Case
package privoro.aid.authenticator.graphql;
import io.aexp.nodes.graphql.GraphQLRequestEntity;
import io.aexp.nodes.graphql.Variable;
import org.junit.Test;
import org.privoro.aid.authenticator.graphql.InputCreate2FA;
import org.privoro.aid.authenticator.graphql.MutationCreate2FA;
import java.net.MalformedURLException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
public class TestGraphQLRequestEntity {
private String EXAMPLE_URL = "https://graphql.example.com";
@Test
public void mutationWithVariables() throws MalformedURLException {
InputCreate2FA input = new InputCreate2FA();
input.setUserID("user-id");
input.setDeviceID("device-id");
input.setRelyingPartyID("relying-party-id");
input.setDescription("description");
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url(EXAMPLE_URL)
.variables(new Variable<InputCreate2FA>("TwoFA", input))
.request(MutationCreate2FA.class)
.build();
System.out.println("Request Entity:" + requestEntity.toString());
assertThat(requestEntity.toString(), containsString("'query ($TwoFA: CreateTwoFA!) { Create2FA (TwoFA: $TwoFA) {"));
}
}
Input variable model
package org.privoro.aid.authenticator.graphql;
public class InputCreate2FA {
public String UserID;
public String RelyingPartyID;
public String Description;
public String DeviceID;
}
Result Model
package org.privoro.aid.authenticator.graphql;
public class TwoFA {
public String UserID;
public String RelyingPartyID;
public String Description;
public String Status;
public String Reason;
public String CreatedTimestamp;
public String ExpiresAt;
public String TwoFAID;
}
MutationCreate2FA
public class MutationCreate2FA {
@GraphQLProperty(name = "Create2FA")
@GraphQLVariables({
@GraphQLVariable(name = "TwoFA", scalar = "CreateTwoFA!")
})
public TwoFAModel twoFa;
}
Output:
query ($TwoFA:CreateTwoFA!){ twoFA : Create2FA(TwoFA:$TwoFA) { Status RelyingPartyID Description CreatedTimestamp UserID TwoFAID ExpiresAt Reason } }
MutationCreate2FA
@GraphQLProperty(name = "Create2FA")
public class MutationCreate2FA {
@GraphQLVariables({
@GraphQLVariable(name = "TwoFA", scalar = "CreateTwoFA!")
})
public TwoFAModel twoFa;
}
Output:
query ($TwoFA:CreateTwoFA!){ Create2FA { twoFA (TwoFA:$TwoFA) { Status RelyingPartyID Description CreatedTimestamp UserID TwoFAID ExpiresAt Reason } } }
MutationCreate2FA
@GraphQLProperty(name = "Create2FA", arguments = {
@GraphQLArgument(name = "TwoFA", value = "$TwoFA")
})
public class MutationCreate2FA extends TwoFA {
}
Output (close, but the builder doesn't inject the variables):
query { Create2FA (TwoFA:$TwoFA) { Status RelyingPartyID Description CreatedTimestamp UserID TwoFAID ExpiresAt Reason } }
If I step back from the docs a little and imagine how I might specify query's and mutations with variables I come up with:
@GraphQLOperation(name = "Create2FAMutation", type="mutation", variables = {
@GraphQLVariable(name = "inputCreate2FA", scalar = "Create2FA!")
}, properties = {
@GraphQLProperty(name = "Create2FA", arguments = {
@GraphQLArgument(name = "TwoFA", variable = "inputCreate2FA")
})
})
class MutationCreate2FA extends TwoFA {
}
Output
mutation Create2FAMutation ($inputCreate2FA: Create2FA!) { Create2FA (TwoFA:$inputCreate2FA) { Status RelyingPartyID Description CreatedTimestamp UserID TwoFAID ExpiresAt Reason } }
I think this preferred usage can be provided and be backwards compatible w/ current usage.
I've introduced an annotation @GraphQLOperation
which is optional (if you don't need variables) in your query/mutation. This would remove the need for @GraphQLVariables
(altogether) and @GraphQLVariable
would only be used in top level operation annotations (not on models or on model properties).
I've also added an optional variable
parameter to the @GraphQLArgument
which associates the argument to a specified variable. The addition allows properties to be defined as arguments that can be bound to variables later which reduces unwanted coupling from the model to the operation.
Can anyone help me with gradle pull request. My gralde projects is not dowloading the nodes jar file.
I added below entry in my gradle build file. Is this the right version?
compile 'io.aexp.nodes.graphql:nodes:0.1.0'
I am not finding anything in Maven repo also.
We made our GraphQLTemplate
a singleton spring bean and noticed some erratic behavior (seemingly confusing the response returned from the server).
The docs suggest new
ing the template up each time so this is our bad, but can we ensure it is thread safe instead? It feels like it fits better with the other XYZTemplate tools you find in spring-land
A template has a handle on a Fetch:
https://github.com/americanexpress/nodes/blob/master/nodes/src/main/java/io/aexp/nodes/graphql/Fetch.java#L54
Which has objectMapper and simpleModule fields that are altered every time send()
is called. I don't have a smoking gun but my hunch is there are some issues stemming from that - two threads have handles on the sample template/fetch but the mapper/modules are being swapped around underneath.
public class DisplayElement {
private String type;
private String text;
private List styles;
private String url;
private List< DisplayElement > displayElements;
private Boolean isSelected;
private Boolean isHighlighted;
}
Hi,
I am trying to use Nodes to query my GraphQL server; this is the query,
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("http://localhost:32806/graphql")
.arguments(new Arguments("dishes", new Argument<String>("chef_id", "chef_1")), new Arguments("dishes", new Argument<String>("name", "Pizza")))
.request(Dish.class)
.build();
System.out.println(requestEntity.getRequest());
GraphQLResponseEntity<Dish> responseEntity = graphQLTemplate.query(requestEntity, Dish.class);
This is the model,
@GraphQLProperty(name="dishes", arguments = {@GraphQLArgument(name = "chef_id"), @GraphQLArgument(name = "dish_id")})
public class Dish
{
private String chef_id;
private String dish_id;
private String name;
private String description;
...getters and setters...
}
This is the schema,
dishes(chef_id: String! dish_id: String name: String limit: Int = 100 offset: Int = 0 columnToSort: SortDishColumn = NAME order: OrderBy = DESCENDING): [Dish]!
The query that Nodes generates is,
query { dishes (chef_id:"chef_1", name:"Pizza") { chef_id name description dish_id } }
The query does work when used with graphiql, and it is exactly what I am expecting from it. However, the code throws an exception every time,
Exception in thread "main" GraphQLException{message='null', status='200', description='Cannot deserialize instance of `org.[... my addition].models.Dish` out of START_ARRAY token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: io.aexp.nodes.graphql.Wrapper["data"])', errors=null}
at io.aexp.nodes.graphql.Fetch.send(Fetch.java:84)
at io.aexp.nodes.graphql.GraphQLTemplate.execute(GraphQLTemplate.java:83)
at io.aexp.nodes.graphql.GraphQLTemplate.query(GraphQLTemplate.java:42)
Any idea of what I am doing wrong?
Thank you for your help.
I use io.aexp.nodes.graphql.nodes version: 0.5.0
for graphQL requests(java client).
While I send requests without header Accept-Encoding = gzip
it works fine. But if I add this header graphQLTemplate throws next exception:
Exception in thread "main" GraphQLException{message='OK', status='200', description='Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens at [Source: (BufferedReader); line: 1, column: 2]', errors=null} at io.aexp.nodes.graphql.Fetch.send(Fetch.java:96) at io.aexp.nodes.graphql.GraphQLTemplate.execute(GraphQLTemplate.java:99) at io.aexp.nodes.graphql.GraphQLTemplate.query(GraphQLTemplate.java:58)
How I have to use gzip compression?
I need use sample with real graphql api
E.g.
{
currency: "USD",
totalAmount: 10.00,
items: [{
name: "test1",
currency: "USD",
price: 5.00
}, {
name: "test2",
currency: "USD",
price: 5.00
}]
}
I've tried it with the code below but items are not generating properly.
InputObject item1 = new InputObject.Builder<>()
.put("name", "test1")
.put("currency", "USD")
.put("price", 5.00)
.build();
InputObject item2 = new InputObject.Builder<>()
.put("name", "test2")
.put("currency", "USD")
.put("price", 5.00)
.build();
List<InputObject> items = new ArrayList<>();
items.add(item1);
items.add(item2);
InputObject input = new InputObject.Builder<>()
.put("currency", "USD")
.put("totalAmount", 10.00)
.put("items", items)
.build();
Actual Output:
mutation { createPayment (input:{currency:"USD",totalAmount:10.00,items:[io.aexp.nodes.graphql.InputObject@74a0c8e4,io.aexp.nodes.graphql.InputObject@82a0c0a5]}) { paymentId } }
Expected Output:
mutation { createPayment (input:{currency:"USD",totalAmount:10.00,items:[{name:"test1",currency:"USD",price:5.00},{name:"test2",currency:"USD",price:5.00}]}) { paymentId } }
Thanks.
Could an example of a mutation be added to the readme?
Currently in the client since we have protected access over the methods in InputObject it becomes impossible to test an argument that has an input object as the value .
If a model contains one or more enum members, GraphQLRequestEntity will loop in the getChildren method, resulting in java.lang.StackOverflowError. Only happens if the enum has at least two contant values.
Our team use this framework to compose query of GraphQL API call. I saw setRequestMethod method in GraphQLRequestEntity is access only package and GraphQLTemplate call it and change request method. For our program, we use Feign+Hystrix as http call. That means we only use Nodes to compose query.
For my understanding, it is not enough to change setRequestMethod as public method. All initial process finish in GraphQLRequestEntity constructor method.
could expose set up requestMethod (query or mutate) in builder?
I think, it would be great to be able to customize the objectmapper instance used in Fetch class. In my case i was missing jackson's java time module to use java time datatypes in my response entities.
Because Fetch class has a final class declaration i can't see any easy way to modify/replace its object mapper instance. This could be useful also in spring (boot) projects, where the environment provides a preconfigured object mapper and an easy way to provide a custom one.
For the moment i helped myself by using @JsonDeserialize annotation for java time properties of my response entities.
I am using this library to test the graphql and I followed the document as well.
I observed that some details are missing in the documents.
I have added the arguments and sending the request but while build the query I am getting Graphql invalid property path error.
E.g.
{
currency: "USD",
totalAmount: 10.00,
items: [{
name: "test1",
currency: "USD",
price: 5.00
}, {
name: "test2",
currency: "USD",
price: 5.00
}]
}
I've tried the code below but items are not being generated properly.
InputObject item1 = new InputObject.Builder<>()
.put("name", "test1")
.put("currency", "USD")
.put("price", 5.00)
.build();
InputObject item2 = new InputObject.Builder<>()
.put("name", "test2")
.put("currency", "USD")
.put("price", 5.00)
.build();
List<InputObject> items = new ArrayList<>();
items.add(item1);
items.add(item2);
InputObject input = new InputObject.Builder<>()
.put("currency", "USD")
.put("totalAmount", 10.00)
.put("items", items)
.build();
Actual Output:
mutation { createPayment (input:{currency:"USD",totalAmount:10.00,items:[io.aexp.nodes.graphql.InputObject@74a0c8e4,io.aexp.nodes.graphql.InputObject@82a0c0a5]}) { paymentId } }
Expected Output:
mutation { createPayment (input:{currency:"USD",totalAmount:10.00,items:[{name:"test1",currency:"USD",price:5.00},{name:"test2",currency:"USD",price:5.00}]}) { paymentId } }
Thanks.
Hi, I'm trying to send a request on a model where I have at the same level two same nodes with identical names and arguments. So I used alias graphQLPropertyName. Problem is I cannot inject the arguments on request. In the given example I want to do:
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("http://graphql.example.com/graphql");
.variables(new Variable("timeFormat", "MM/dd/yyyy"))
.arguments(new Arguments("path.to.argument.property",
new Argument("id", "toto")))
.arguments(new Arguments("path.to.argument.propertyWithDifferentAlias",
new Argument("id", "titi")))
.request(SampleModel.class)
.build();
GraphQLResponseEntity<SampleModel> responseEntity = graphQLTemplate.query(requestEntity, SampleModel.class);
However the API does not validate my query (while it has the proper syntax). Here is a simplified error message:
Unrecognized field "propertyWithDifferentAlias" (class SampleModel), not marked as ignorable ( known properties: "property])
How can I inject "id" "titi" to my aliased property?
GraphQL servers can format the errors any way they want, defined here
Currently io.aexp.nodes.graphql.internal.Error
is very rigid and only matches the default message and locations fields. It would be nice to make this class extensible so users can match the server's error format.
Do we have any support for using the Union in the response?
This is the sample query I am trying to fetch the response for .
searchABCs (pageSize:100,pageNum:1,query:"{}")
{
totalCount
results{
... on ABC {
id
name
value
abcInnerItem{
id
name
}
}
}
}
This is already tracked here:
#62
Please let me know if there is an alternative way of getting the response for this.
I've tried to run simple GraphQL query returning a list:
query { allTeams { name } }
Here is the DTO class that I use to build a GraphQLRequestEntity:
@GraphQLProperty(name = "allTeams")
public class AllTeamsQuery {
String name;
@GraphQLIgnore
@GraphQLProperty(name="allTeams")
List<TeamQuery> allTeams;
... getters and setters
The GraphQL query is good and run successfully against the server. However the result is not deserialized properly: Cannot deserialize instance of io.superherosample.model.AllTeamsQuery
out of START_ARRAY token which seems to be a Jackson problem.
I guess ST is wrong in my DTO class. I've tried many options and I'm totally blocked. It would be nice that the documentation provides some examples on how to fetch lists.
I reuse custom types in my model class, for example a class called CreditCardNumber. Static fields in this class are also send with the request. On server side, the validation failed because of that static field is not defined in the schema.
public class CreditCardNumber {
private static final Pattern REGEX = Pattern.compile("\\d{12;19}");
private String number;
...
}
Because I share my custom types across different projects, I'm not able to annote the static field with @GraphQLIgnore.
How should I handle this? Is it a bug, that static fields are in included? May Mixins as used by Jackson be a possible solution?
I'm interested in this project but have a lot invested in RestTemplate and proprietary infra below that. I would need to be able to provide my own Fetch implementation for this to be usable.
mutation {
createUser(
userData: {
firstName: "Bullhorn", lastName: "Pegas"
}) {
user{
firstName
lastName
}
}
}
InputObject inputObject = new InputObject.Builder<>().put("firstName", "myname").put("lastName", "Sujith")
.build();
GraphQLRequestEntity build = GraphQLRequestEntity.Builder().url("http://192.168.0.182:8000/graphql/")
.requestMethod(GraphQLMethod.MUTATE)
.arguments(new Arguments("createUser", new Argument<>("userData", inputObject)))
.request(TestClass.class).build();
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
System.err.println("Sending request " + build.getRequest());
GraphQLResponseEntity execute = graphQLTemplate.mutate(build, TestResponse.class);
import io.aexp.nodes.graphql.annotations.GraphQLArgument;
import io.aexp.nodes.graphql.annotations.GraphQLProperty;
@getter
@Setter
@GraphQLProperty(arguments = {
@GraphQLArgument(name = "firstName"),
@GraphQLArgument(name = "lastName")
},name = "createUser")
public class TestResponse {
private String firstName;
private String lastName;
}
import io.aexp.nodes.graphql.annotations.GraphQLArgument;
import io.aexp.nodes.graphql.annotations.GraphQLProperty;
@GraphQLProperty(name = "createUser", arguments = { @GraphQLArgument(name = "userData") })
@getter
@Setter
public class TestClass {
private String firstName;
private String lastName;
}
Requests are being built through reflection on every transaction. Adding the ability to cache the requests upon completion and using already built requests in future transactions could improve the performance
Looking for some help.. I am using this library into my project. and getting error while deserializing the response.
My query :
query somethingHere {
courses {
id
}
}
Response :
{
"data": {
"courses": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
]
}
}
I have defined the class like this
@Setter
@getter
@GraphQLProperty(name = "courses")
public class Course {
int id;
}
@getter
@Setter
public class CourseRes{
private List courses;
}
try{
final GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("https://nimbus-service.herokuapp.com/courses")
.request(Course.class)
.build();
GraphQLResponseEntity<CourseRes> tmp = new GraphQLTemplate().query(requestEntity, CourseRes.class);
}catch(Exception e) {
//throw new VirtualAssistantException(e.getMessage(), e);
e.printStackTrace();
}
}
Getting below exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "courses" (class CourseRes), not marked as ignorable (0 known properties: ])
at [Source: (String)"{"courses":[{"id":1},{"id":2},{"id":3}]}"; line: 1, column: 13] (through reference chain: io.aexp.nodes.graphql.Wrapper["data"]->CourseRes["courses"])
Thanks
I'm trying to create a small demo project using this project to see if this fits our needs. It is nothing fancy just a bare bones app. The app just assembles the request object and prints it to the console.
When trying to assemble add the arguments to the builder using the "GraphQLRequestEntity.Builder.arguments()" method I get a null pointer exception. The stack trace looks like this:
java.lang.NullPointerException
at io.aexp.nodes.graphql.GraphQLRequestEntity.setArguments(GraphQLRequestEntity.java:119)
at io.aexp.nodes.graphql.GraphQLRequestEntity.<init>(GraphQLRequestEntity.java:62)
at io.aexp.nodes.graphql.GraphQLRequestEntity$RequestBuilder.build(GraphQLRequestEntity.java:379)
at App.main(App.java:36)
I have included what I think are the correct annotations on the class I'm trying to query for. Below is the main method for my "app" and the first few lines of the model I'm using.
Is there something I'm doing wrong? Thanks in advance.
`public class App {
public String getGreeting() {
return "Hello world.";
}
public static void main(String[] args) {
try{
System.out.println(new App().getGreeting());
GraphQLTemplate template = new GraphQLTemplate();
Argument<Fudge> ag= new Argument("thing", "d070633a9f9"); //<---- this throws a warning aobut argumant being a generic an i need to do a checked cast but it gets mad when I do that
Arguments arguments= new Arguments("thing1.thing2.Fudge",ag);
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("http://graphql.example.com/graphql")
.arguments(arguments)//<---- this seems to be what fails. If i comment it out It works but I have no arguments
.request(Fudge.class)
.build();
System.out.println(requestEntity.getRequest());
System.out.println("no errors, this time");
}
catch(Exception ex){ex.printStackTrace();
}
}
}`
@GraphQLProperty(name="fudge"
, arguments={
@GraphQLArgument(name="id", type="String")
,@GraphQLArgument(name="thing", type="String")
,@GraphQLArgument(name="bar", type="String")
}
)
public class Fudge {
String id;
String subjectLine;
String body;
}
// everything below here is just getters and setters
When using Nodes to call an API where arguments are mutually exclusive, there is currently no way to leave out either argument even if value is not set or explicitly set to null in annotation. This is due to two issues:
a) null values are treated as String("null") instead of null values internally, while parsing value from GraphQLArgument annotation
and
b) the GraphQLArgument annotation has no notion of "!"/optional argument or not, meaning that request generation can effectively never leave out any arguments.
An example is an electricity consumption API where parameters first or last is used to get the first or last N entries from a series of consumption values. The API returns nothing on the following conditions:
consumption(resolution: HOURLY, first: null, last: 100)
or consumption(resolution: HOURLY, first: 100, last: null)
The query must be either of:
consumption(resolution: HOURLY, last: 100)
or consumption(resolution: HOURLY, first: 100)
`@GraphQLProperty(name = "business")
public class MerchantFinancingLoanSearchBusiness {
String legalBusinessName;
@GraphQLProperty(name = "federalTaxId")
String tin;
@GraphQLProperty(name = "businessName")
String DBA;`
gives you:
business { DBA : businessNametin : federalTaxIdlegalBusinessName }
Here is the request builder
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder().headers(uriHeaders) .request(MerchantFinancingLoanSearchLoanAccounts.class) .arguments((new Arguments("findLoanAccounts", new Argument("start", Integer.parseInt(loanAccountsSearchRequest.getOffset())), new Argument("end", Integer.parseInt(loanAccountsSearchRequest.getLimit())), new Argument("criteria", criteriaInput)))) .url(configData.getProperty("loanAccountssearch.url", "")).build();
Im trying to use the GraphQL API and we have the need to deserialize a field that is in String to LocalDateTime and in that case ,we get the below exception
"message" : "Servlet.service() for servlet [dispatcherServlet] in context with path [] threw
exception [Request processing failed; nested exception is GraphQLException{message='null',
status='200', description='Cannot deserialize value of type `java.time.LocalDateTime` from
String \"2020-01-30T13:42:34.400\": Failed to deserialize java.time.LocalDateTime:
(java.time.format.DateTimeParseException) Text '2020-01-30T13:42:34.400' could not be
parsed at index 10\n at [Source: UNKNOWN; line: -1, column: -1] (through reference chain:
io.aexp.nodes.graphql.Wrapper[\"data\"]-
>com.ing.diba.scalable.domain.inbound.PortFolioResponse[\"realTimeValuation\"]-
>com.ing.diba.scalable.domain.inbound.RealTimeValuationResponse[\"dateTime\"])',
errors=null}]
The code to invoke is
public GraphQLResponseEntity<EntityResponse> getEntity(Long
entityId) {
GraphQLResponseEntity<EntityResponse> responseEntity = new
GraphQLRequestEntity requestEntity = null;
try {
requestEntity = GraphQLRequestEntity.Builder()
.url(portFolioOverViewProperties.getConnectionUrl())
.request(Entity.class)
.arguments(new Arguments("entityOverview", new Argument("id", entityId)))
.build();
} catch (MalformedURLException e) {
log.error("MalFormed URL {}", e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,
e.getMessage(), e);
}
responseEntity = graphQLTemplate.query(requestEntity, Entity.class);
return responseEntity;
}
On debugging we found that the exception occurs from the deserialize method of
LocalDateTimeDeserializer class of jacksonjar at highlighted line
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws
IOException {
if (parser.hasTokenId(6)) {
String string = parser.getText().trim();
if (string.length() == 0) {
return !this.isLenient() ? (LocalDateTime)this._failForNotLenient(parser, context,
JsonToken.VALUE_STRING) : null;
} else {
try {
if (this._formatter == DEFAULT_FORMATTER && string.length() > 10 &&
string.charAt(10) == 'T') {
return string.endsWith("Z") ? LocalDateTime.ofInstant(Instant.parse(string),
ZoneOffset.UTC) : LocalDateTime.parse(string, DEFAULT_FORMATTER);
} **else {
return LocalDateTime.parse(string, this._formatter);
}**
} catch (DateTimeException var12) {
return (LocalDateTime)this._handleDateTimeException(context, var12, string);
}
}
But if we parse it locally we don't get the exception
LocalDateTime responseDateTime = LocalDateTime.parse(realTimeValuation.getDateTime());
Can you let me know what is causing this issue? and so its possible to resolve it in our application
Hi Team, you have a very nice client for graphQL. Do you have any plans to publish to Maven Central?
The client seems to be using httpurlconnection to make the graph ql api calls . This restricts making calls to https .
I have a mutation query which returns a Boolean. The following POJO does not work:
@GraphQLProperty(name = "updateHealth", arguments = {@GraphQLArgument(name = "request")})
`public class UpdateHealth {
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}`
I get the following error: Validation error of type SubSelectionNotAllowed: Sub selection not allowed on leaf type Boolean of field updateHealth @ 'updateHealth''
.
It generates the following query: query { updateHealth (request:{apiStatusDetails:[{apiPath:"/v1/sampleextensionapi/health",httpMethod:"GET",status:AVAILABLE},{apiPath:"/v1/sampleextensionapi/sampleresource/hello",httpMethod:"GET",status:AVAILABLE}],extName:"hello-world",status:AVAILABLE}) { isSuccess } }
On the graphql console, when I run the query WITHOUT {isSuccess}
at the end, it works. How do I do it programmatically? Here is how I am setting things up:
`List apiDetails = new ArrayList();
for (ApiStatusDetails detail : response.getApiStatusDetails()) {
InputObject apiDetail = new InputObject.Builder().put("apiPath", detail.getApiPath())
.put("httpMethod", detail.getHttpMethod()).put("status", detail.getStatus()).build();
apiDetails.add(apiDetail);
}
InputObject request = new InputObject.Builder().put("extName", response.getExtensionName())
.put("status", response.getStatus()).put("apiStatusDetails", apiDetails).build();
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url("http://localhost:8443/v1/console/graphql")
.arguments(new Arguments("updateHealth", new Argument<>("request", request)))
.request(UpdateHealth.class)
.build();
System.out.println(requestEntity.getRequest());
GraphQLResponseEntity responseEntity = graphQLTemplate.mutate(requestEntity, UpdateHealth.class);`
Hi,
My variables don't seem to be taken into account when executing the query
List<Variable> myVars = new ArrayList<Variable>();
myVars.add(new Variable<String>("name", "text"));
myVars.add(new Variable<String>("language", "en"));
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url(TestGlobalConfiguration.getGraphQlUrl())
.variables(myVars).request(Jcr.class)
.build();
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
GraphQLResponseEntity<Jcr> responseEntity = graphQLTemplate.query(requestEntity, Jcr.class);
In the Model I have defined, one of the property has the variables definition:
@GraphQLVariables({@GraphQLVariable(name = "name", scalar = "String"),
@GraphQLVariable(name = "language", scalar = "String")})
private Property property;
However when executing the request I can see the query is:
query ($name:String,$language:String){ jcr { nodeByPath { marketingFactory { optimizationTestVariant { path name property (name:$name,language:$language) { value } uuid } personalizedVariant { path name property (name:$name,language:$language) { value } uuid } } name uuid } } }
Which obviously returns:
Validation error of type VariableTypeMismatch: Variable type doesn't match since $name is not the expected "text"
I used to use GraphQLArguments, which worked fine but when you have the same arguments at two different places it is recommended to use Variables.
What am I doing wrong?
Hi I am trying to query an API which is of the following format.
query{ requestallstudentdata{ id name hobby } }
In response we get something like following:
`{
"data": {
"requestallstudentdata": [
{
"id": 1,
"name":"xyz",
"hobby":"fxdu"
},
{
"id": 2,
"name":"xYz",
"hobby":"fdu"
}
]
}
}`
How to handle such requests.Please assist.
Currently I have the following classes.
I am getting the following response whenever I am trying to send a list.
GraphQLException{message='OK', status='200', description='Can not deserialize instance of com.graphql.model.foo.StudentDetails out of START_ARRAY token
at [Source: N/A; line: -1, column: -1] (through reference chain: io.aexp.nodes.graphql.Wrapper["data"])', errors=null}
When running Main.kt in the kotlin-sample my coworker and I are both seeing the following error:
Exception in thread "main" GraphQLException{message='OK', status='200', description='Cannot construct instance of `models.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: io.aexp.nodes.graphql.Wrapper["data"])', errors=null}
Currently dependent on Jackson 2.9.6 which has some vulnerabilities (e.g. https://nvd.nist.gov/vuln/detail/CVE-2018-19362)
Please consider updating to 2.9.8 as these vulnerabilities have been resolved in this version.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.