Quarkus SmallRye Graphql-Client Mutation Query - graphql

I try to execute a Graphql Client Query. Sadly I am not able to find any kind of documentation or examples on how to do a simple Mutation using the Dynamic Graph QL Client. Here is the documentation https://quarkus.io/guides/smallrye-graphql-client.
mutation mut {
add(p: {
amount: {0},
fromCurrencyId: {1},
reference: {2},
terminalKey: {3},
toCurrencyId: {4}
}) {
address
toCurrencyAmount
rate
createdAt
expireAt
}
}
{0}..{4} are variable place holder.
Does someone know how to execute this query with the DynamicGraphlQlClient?
Thanks!

Having declared your server side mutation following the Eclipse MicroProfile API as follows:
#GraphQLApi
#ApplicationScoped
public class MyGraphQLApi {
#Mutation
public OutputType add(#Name("p") InputType p)) {
// perform your mutation and return result
}
}
You can then use the DynamicGraphQLClient declaratively to perform the mutation using the DynamicGraphQLClient#executeSync method with a io.smallrye.graphql.client.core.Document constructed after your mutation structure:
#Inject
private DynamicGraphQLClient client;
public void add() {
Document document = Document.document(
operation(
OperationType.MUTATION,
"mut",
field(
"add",
arg(
"p",
inputObject(
prop("amount", "amountValue"),
prop("fromCurrencyId", "fromCurrencyIdValue"),
prop("reference", "referenceValue"),
prop("terminalKey", "terminalKeyValue"),
prop("toCurrencyId", "toCurrencyIdValue")
)
)),
field("address"),
field("toCurrencyAmount"),
field("rate"),
field("createdAt"),
field("expireAt")
)
);
JsonObject data = client.executeSync(document).getData();
System.out.println(data.getString("address"));
}

Related

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.

Why graphql-js executor stops resolving child fields that have their own resolvers when the parent resolver return null/undefined?

While writing a lib for GraphQL in JavaScript I stumbled upon a curious behavior. I managed to isolate it in a very simple example. Let's take this server snippet:
const { ApolloServer, gql } = require("apollo-server")
const typeDefs = gql`
type Book {
resolveItSelf: String
}
type Query {
book: Book
}
`
const resolvers = {
Query: {
book: () => {
return null // same behavior with undefined here
}
},
Book: {
resolveItSelf: () => "resolveItSelf"
}
}
const server = new ApolloServer({ typeDefs, resolvers })
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`)
})
If we query this server with the following query:
{
book {
resolveItSelf
}
}
We get this result:
{
"data": {
"book": null
}
}
So, I was expecting the graphql executor to try to resolve the "resolveItSelf" field (which have its own resolver) even if the book resolver returned null.
A way to get the behavior I expect is to change the book's resolver a little bit:
const resolvers = {
Query: {
book: () => {
return {} // empty object instead of null/undefined
}
},
Book: {
resolveItSelf: () => "resolveItSelf"
}
}
Then we get this result:
{
"data": {
"book": {
"resolveItSelf": "resolveItSelf"
}
}
}
The field is resolved even if the parent is empty !
So my question is why the graphql-js executor stop trying to resolve fields if the parent's resolver return null/undefined, even though requested fields can be resolved on their own ? (Is there a section in the draft that cover this ?)
In GraphQL, null represents a lack of a value. If a field resolves to null, it doesn't make sense for its "child" field resolvers' to be called since they wouldn't be returned in the response anyway.
From the Value Completion section of the spec (emphasis mine):
If the fieldType is a Non‐Null type:
a. Let innerType be the inner type of fieldType.
b. Let completedResult be the result of calling CompleteValue(innerType, fields, result, variableValues).
c. If completedResult is null, throw a field error.
d. Return completedResult.
If result is null (or another internal value similar to null such as undefined or NaN), return null.
If fieldType is a List type:
a. If result is not a collection of values, throw a field error.
b. Let innerType be the inner type of fieldType.
c. Return a list where each list item is the result of calling CompleteValue(innerType, fields, resultItem, variableValues), where resultItem is each item in result.
If fieldType is a Scalar or Enum type:
a. Return the result of “coercing” result, ensuring it is a legal value of fieldType, otherwise null.
If fieldType is an Object, Interface, or Union type:
a. If fieldType is an Object type.
i. Let objectType be fieldType.
b. Otherwise if fieldType is an Interface or Union type.
i. Let objectType be ResolveAbstractType(fieldType, result).
c. Let subSelectionSet be the result of calling MergeSelectionSets(fields).
d. Return the result of evaluating ExecuteSelectionSet(subSelectionSet, objectType, result, variableValues) normally (allowing for parallelization).
In other words, even if a field's type is an Object (and therefore has a selection set of fields that may also be resolved), if it resolves to null, no further execution happens along that path.

How to create generics with the schema language?

Using facebook's reference library, I found a way to hack generic types like this:
type PagedResource<Query, Item> = (pagedQuery: PagedQuery<Query>) => PagedResponse<Item>
​
interface PagedQuery<Query> {
query: Query;
take: number;
skip: number;
}
​
interface PagedResponse<Item> {
items: Array<Item>;
total: number;
}
function pagedResource({type, resolve, args}) {
return {
type: pagedType(type),
args: Object.assign(args, {
page: { type: new GraphQLNonNull(pageQueryType()) }
}),
resolve
};
function pageQueryType() {
return new GraphQLInputObjectType({
name: 'PageQuery',
fields: {
skip: { type: new GraphQLNonNull(GraphQLInt) },
take: { type: new GraphQLNonNull(GraphQLInt) }
}
});
}
function pagedType(type) {
return new GraphQLObjectType({
name: 'Paged' + type.toString(),
fields: {
items: { type: new GraphQLNonNull(new GraphQLList(type)) },
total: { type: new GraphQLNonNull(GraphQLInt) }
}
});
}
}
But I like how with Apollo Server I can declaratively create the schema. So question is, how do you guys go about creating generic-like types with the schema language?
You can create an interface or union to achieve a similar result. I think this article does a good job explaining how to implement interfaces and unions correctly. Your schema would look something like this:
type Query {
pagedQuery(page: PageInput!): PagedResult
}
input PageInput {
skip: Int!
take: Int!
}
type PagedResult {
items: [Pageable!]!
total: Int
}
# Regular type definitions for Bar, Foo, Baz types...
union Pageable = Bar | Foo | Baz
You also need to define a resolveType method for the union. With graphql-tools, this is done through the resolvers:
const resolvers = {
Query: { ... },
Pageable {
__resolveType: (obj) => {
// resolve logic here, needs to return a string specifying type
// i.e. if (obj.__typename == 'Foo') return 'Foo'
}
}
}
__resolveType takes the business object being resolved as its first argument (typically your raw DB result that you give GraphQL to resolve). You need to apply some logic here to figure out of all the different Pageable types, which one we're handling. With most ORMs, you can just add some kind of typename field to the model instance you're working with and just have resolveType return that.
Edit: As you pointed out, the downside to this approach is that the returned type in items is no longer transparent to the client -- the client would have to know what type is being returned and specify the fields for items within an inline fragment like ... on Foo. Of course, your clients will still have to have some idea about what type is being returned, otherwise they won't know what fields to request.
I imagine creating generics the way you want is impossible when generating a schema declaratively. To get your schema to work the same way it currently does, you would have to bite the bullet and define PagedFoo when you define Foo, define PagedBar when you define Bar and so on.
The only other alternative I can think of is to combine the two approaches. Create your "base" schema programatically. You would only need to define the paginated queries under the Root Query using your pagedResource function. You can then use printSchema from graphql/utilities to convert it to a String that can be concatenated with the rest of your type definitions. Within your type definitions, you can use the extend keyword to build on any of the types already declared in the base schema, like this:
extend Query {
nonPaginatedQuery: Result
}
If you go this route, you can skip passing a resolve function to pagedResource, or defining any resolvers on your programatically-defined types, and just utilize the resolvers object you normally pass to buildExecutableSchema.

Is it possible to not return any data when using a GraphQL mutation?

I have several GraphQL queries and mutations, now I'm trying to implement a delete mutation without returning any data:
type Mutation{
addElement(element: ElementData): ID
removeElement(id: ID): ¿?
}
However, it seems to be required to have a return value for the delete operation. Is there a way to perform an "empty" response in GraphQL? I would like to avoid things like returning a boolean or status flag if possible.
I'm not sure on what are the best practices for GraphQL delete operations.
According to this Github issue you cannot return nothing.
You can define a return type which is nullable e.g.
type Mutation {
addElement(element: ElementData): ID
removeElement(id: ID): Boolean
}
But I suggest you return the id of the deleted element, because if you want to work with a cached store you have to update the store when the delete mutation has ran successfully.
(A) Solution with graphql-scalars
The original answer is below.
Here is another one solution with graphql-scalars library:
install npm install graphql-scalars and then
import their Void type: https://www.graphql-scalars.dev/docs/scalars/void
(B) Solution with a custom scalar
Note: design with void-result from mutations goes against "GQL best practices"
This example was written for NodeJS Apollo Framework, but it is pretty easy to convert the implementation for your language/framework
I'm pretty sure: there is an NPM-package named graphql-void but if you don't want to add another one dependency just copy this code.
1. define Void-scalar in your schema
# file: ./schema.gql
scalar Void
2. implement resolver
// file ./scalar-void.js
import { GraphQLScalarType } from 'graphql'
const Void = new GraphQLScalarType({
name: 'Void',
description: 'Represents NULL values',
serialize() {
return null
},
parseValue() {
return null
},
parseLiteral() {
return null
}
})
export Void
3. add the resolver to ApolloServer
Add the Void resolver to the options of your instance of Apollo Server:
# file: ./server.js
import { ApolloServer } from 'apollo-server-express'
import { Void } from './scalar-void'
const server = new ApolloServer({
typeDefs, // use your schema
resolvers: {
Void: Void,
// ... your resolvers
},
})
4. use Void for your mutations in the schema
Finally, use the new scalar in your schema:
# file: ./schema.gql
type Mutation{
addElement(element: ElementData): ID
removeElement(id: ID): Void
}
If you use TypeScript and graphql-codegen:
In the GraphQL schema:
scalar Void
type Mutation {
removeElement(id: ID): Void
}
In the codegen config for resolvers:
config:
scalars:
Void: "void"
With this config TypeScript will ensure that nothing is returned from the removeElement mutation resolver. And the returning value for the mutation will always be null on the GraphQL side.
Check out graphql-scalars Void. This is standard boilerplate for all of my GraphQL projects.
npm i graphql-scalars

Cannot get Relay to make GraphQL Call over the network

I am new to Relay, and I am having problems making it work with a GraphQL server.
I have adapted the Tea sample from the relay homepage to the SWAPI relay service. I cloned swapi-graphql, and added cors to the express server. I tested the link with this code:
var query = `
{
allFilms {
edges {
node {
id
}
}
}
}
`
fetch("http://localhost:50515/graphiql?query=" + query)
.then(response=>response.json())
.then(json=>console.log(json))
I got a response from the server, I saw some network action, it worked! I can communicate with the graphiql service.
Next, I created a query that was structured similar to the TeaStoreQuery. I tested it, and it returned the expected results.
query AllFilmQuery {
allFilms {
...filmListFragment
}
}
fragment filmListFragment on FilmsConnection {
edges {
node {
...filmFragment
}
}
}
fragment filmFragment on Film {
id
title
releaseDate
}
HOW DO YOU MAKE THIS WORK WITH RELAY??
I cannot figure out how to use Relay to query the server. Here is the code that I adapted from the Tea sample.
import { render } from 'react-dom'
import {
RootContainer,
createContainer,
Route,
injectNetworkLayer
} from 'react-relay'
// React component for each star wars film
const Film = ({ id, title, releaseDate }) =>
<li key={id}>
{title} (<em>{releaseDate}</em>)
</li>
// Relay container for each film
const FilmContainer = createContainer(Film, {
fragments: {
film: () => Relay.QL`
fragment on Film {
id,
title,
releaseDate
}
`
}
})
// React component for listing films
const FilmList = ({ films=[] }) =>
<ul>
{films.map(
film => <Film {...film} />
)}
</ul>
// Relay container for Listing all Films
const FilmListContainer = createContainer(FilmList, {
fragments: {
films: () => Relay.QL`
fragment on FilmsConnection {
edges {
node {
${ Film.getFragment('film') }
}
}
}
`
}
})
// The Home Route
class FilmHomeRoute extends Route {
static routeName = 'Home'
static queries = {
allFilms: (Component) => Relay.QL`
query AllFilmQuery {
allFilms {
${Component.getFragment('allFilms')}
}
}
`
}
}
// Is this how you setup a network layer
// I am using CORS, and I testing the graphql service with fetch
// The graphql service works but Relay never seems to try to connect
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer('http://localhost:50515/graphiql')
)
render(
<RootContainer
Component={FilmListContainer}
route={new FilmHomeRoute()}
/>,
document.getElementById('react-container')
)
When I run this sample (source | output) I do not see any attempts at making network requests. I do see an error "Cannot render map of null". It seems like it cannot map the allfilms data.
What am I doing wrong?
According to section 5.1 of this document, the "Relay Object Identification Specification":
Relay‐compliant servers may expose root fields that are not plural identifying root fields; the Relay client will just be unable to use those fields as root fields in its queries.
Based on this specification, Relay can not query plural fields at the root of a query unless the field takes a list of arguments that exactly maps to the results. That means the allFilms field cannot be used with Relay. You can read more about the limitations in this other StackOverflow answer: How to get RelayJS to understand that a response from GraphQL is an array of items, not just a single item
If you would like to have a GraphQL schema with root fields that return arrays, you might want to use a different GraphQL client, since Relay is particularly restrictive in what kinds of schemas it works with. graphql.org has a list: http://graphql.org/code/#graphql-clients

Resources