GraphQLError Unknown type "XYZMutationInput" - graphql

How to create graphql input type for DRF serializer?
I am using django rest framework (DRF) serializers, graphene-django, and I am able to see the CreateThingMutationInput type defined in graphiql:
mutation TestCreate($input: CreateThingMutationInput!) {
createProjectThing(input: $input) {
id
errors {
field
messages
}
}
}
However, I am unable to run:
schema = graphene.Schema(query=Query)
result = schema.execute(self.query, variables=variables)
I get:
[GraphQLError('Unknown type "CreateThingMutationInput".',)]
With the following:
class CreateThingMutation(SerializerMutation):
class Meta:
serializer_class = ThingListViewSerializer
class Mutation(graphene.ObjectType):
debug = graphene.Field(DjangoDebug, name="_debug")
create_project_thing = CreateThingMutation.Field()
I've also tried:
class CreateThingMutationInput(graphene.ObjectType):
input = graphene.Field(convert_serializer_to_input_type(ThingListViewSerializer))
As well as trying to define a:
class Input:
input = graphene.Field(convert_serializer_to_input_type(ThingListViewSerializer))
I can also see the type defined from graphql-codegen in types.d.ts:
export type CreateThingMutationInput = {
id?: Maybe<Scalars['Int']>,
...
}
related:
https://github.com/graphql-python/graphene/issues/531
https://github.com/graphql-python/graphene-django/blob/master/docs/mutations.rst#django-rest-framework
https://github.com/graphql-python/graphene-django/issues/121

I forgot to add the mutation kwarg to:
schema = graphene.Schema(query=Query)
Should be:
schema = graphene.Schema(query=Query, mutation=Mutation)
Another reason this can happen for GraphQLError('Unknown type "Number".',) is if a query function gets an unexpected argument, for example calling getThing with a Number, instead of an ID:
query TestQueryWontWork(id: Number="") {
getThing(id: $id)
}
query TestQueryWorks(id: ID!) {
getThing(id: $id)
}

Related

How to find path to a field in a graphql query

I am very new to graphql. I have a following graphql query for an example:
query pets {
breed(some arguments)
{
name
items
{
owner(some arguments)
{
items
{
ID
ownerSnumber
country
address
school
nationality
gender
activity
}
}
name
phoneNumber
sin
}
}
}
Is it possible to parse a gql query and get the path of a field in the query?
For example I would like to get the path of 'ID'. For example from the above query, is it possible to get the path where the ID is: owner.items.ID
With https://graphql.org/graphql-js/ it exposes a fourth argument called resolve info. This field contains more information about the field.
Have a look at GraphQLObjectType config parameter type definition:
With a good start from the earlier answer, relying on the ResolveInfo you could do something like a recursive check going from child to parent:
export const getFieldPath = (path: Path): string => {
if (!path.prev) return `${path.key}`
return `${getFieldPath(path.prev)}.${path.key}`
}
And later in your resolver you could use it like:
const myFieldResolver = (parent, args, ctx, info) => {
const pathOfThisResolversField = getFieldPath(info.path)
// use your pathOfThisResolversField
return yourFieldResolvedData
};
Worth noting though, the solution above will include every node all the way to the query root, rather than just the ones you mentioned owner.items.ID

GraphQL register new Type wiring with argument to data fetcher

Having a GraphQL schema:
type TypeA {
id: ID,
name: String,
other: TypeC
}
type TypeB {
id: ID,
name: String,
other: TypeC
}
How should I implement TypeC wiring independently from source object type? I know I can do:
RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("TypeA")
.dataFetcher("other", dataFetcher_typeC.get())
.type(TypeRuntimeWiring.newTypeWiring("TypeB")
.dataFetcher("other", dataFetcher_typeC.get())
.build()
but then the data fetcher is dependant on a source object type:
DataFetcher<CompletableFuture<Collection<TypeC>>> get() {
return dataFetchingEnvironment -> {
<??> sourceObj = dataFetchingEnvironment.getSource();
return getObject(sourceObj.someProperty);
};
}
Given both POJOs (TypeA and TypeB) have reference field to TypeC, how to resolve TypeC field by given reference, not source object?
I have actually figured out two possible solutions to the problem:
When defining new wiring, get source object and from it the field. Call dataFetcher method with parameter, like regular java method:
Inside data fetcher get field name from DataFetcherEnvironment. Use reflection to get field from source object
Example #1:
RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("TypeA")
.dataFetcher("other", environment -> {
TypeA sourceObj = environment.getSource();
return dataFetcher_typeC.get(sourceObj.other)})
.type(TypeRuntimeWiring.newTypeWiring("TypeB")
TypeB sourceObj = environment.getSource();
return dataFetcher_typeC.get(sourceObj.other)})
.build()
Example #2:
DataFetcher<CompletableFuture<Collection<TypeC>>> get() {
return dataFetchingEnvironment -> {
Field declaredField = dataFetchingEnvironment.getSource().getClass()
.getDeclaredField(dataFetchingEnvironment.getField().getName());
declaredField.setAccessible(true);
String value = (String) declaredField.get(dataFetchingEnvironment.getSource());
return getObject(sourceObj.someProperty);
};
}
Second option looks better but still unsure if this is correct approach.
From the documentation here
the dataFetchingEnvironment provides getExecutionStepInfo() method which returns the ExecutionStepInfo object. From there, you can get the parent information.
ExecutionStepInfo executionStepInfo = environment.getExecutionStepInfo();
ExecutionStepInfo parentInfo = executionStepInfo.getParent();
GraphQLObjectType parentType = (GraphQLObjectType) parentInfo.getUnwrappedNonNullType();
// parentType.getName() returns you "TypeA" or "TypeB"

How to trigger visitInputObject method on custom directive?

I'm building a custom directive in which I'm hoping to validate entire input objects. I'm using the INPUT_OBJECT type with the visitInputObject method on SchemaDirectiveVisitor extended class.
Every time I run a mutation using the input type then visitInputObject does not run.
I've used the other types/methods like visitObject and visitFieldDefinition and they work perfectly. But when trying to use input types and methods they will not trigger.
I've read all the available documentation I can find. Is this just not supported yet?
Some context code(Not actual):
directive #validateThis on INPUT_OBJECT
input MyInputType #validateThis {
id: ID
someField: String
}
type Mutation {
someMutation(myInput: MyInputType!): SomeType
}
class ValidateThisDirective extends SchemaDirectiveVisitor {
visitInputObject(type) {
console.log('Not triggering');
}
}
All the visit methods of a SchemaDirectiveVisitor are ran at the same time -- when the schema is built. That includes visitFieldDefinition and visitFieldDefinition. The difference is that when we use visitFieldDefinition, we often do it to modify the resolve function for the visited field. It's this function that's called during execution.
You use each visit methods to modify the respective schema element. You can use visitInputObject to modify an input object, for example to add or remove fields from it. You cannot use it to modify the resolution logic of an output object's field. You should use visitFieldDefinition for that.
visitFieldDefinition(field, details) {
const { resolve = defaultFieldResolver } = field
field.resolve = async function (parent, args, context, info) {
Object.keys(args).forEach(argName => {
const argDefinition = field.args.find(a => a.name === argName)
// Note: you may have to "unwrap" the type if it's a list or non-null
const argType = argDefinition.type
if (argType.name === 'InputTypeToValidate') {
const argValue = args[argName]
// validate here
}
})
return resolve.apply(this, [parent, args, context, info]);
}
}

how do use type-graphql argstype object as a parameter for sequelize findall?

I use nestjs, sequelize-typescript, and type-graphql
I created type-graphql #ArgsType
#ArgsType()
export class TagArgs{
#Field(type => Int)
type: number;
}
and in my resolver, i create query like following using above TagArgs
#Query(returns => [Tag])
tags(#Args() tagArgs: TagArgs): Promise<Tag[]>{
return this.tagService.findAll(tagArgs);
}
following is my findAll function of class TagService
async findAll(tagArgs: TagArgs): Promise<Tag[]>{
return this.tagRepository.findAll<Tag>(tagArgs);
}
But Errors such as TS:2345: Argument of type 'TagArgs' is not assignable to parameter of type 'FindOptions' occur at this.tagRepository.findAll(tagArgs)
how can i use ArgsType object??
Assuming that TagArgs properties have the same signature that Tag entity class, you should do something like that:
this.tagRepository.findAll<Tag>({ where: tagArgs })

clean way to get same field by different key

Here is the problem. I can get member by ID and my query looks like below:
{
member(memberId:[1,2]) {
firstName
lastName
contacts {
}
}
}
Now I need to add few more query to get member by name and email like below
{
member(email:["abc#xy.com","adc#xy.com"]) {
firstName
lastName
contacts {
}
}
}
{
member(name:["abc","adc"]) {
firstName
lastName
contacts {
}
}
}
How do I design my graphQL query and schema? Should my query have just 1 field with multiple optional arguments? like below
Field("member", ListType(Member),
arguments = ids :: email :: name,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Or should I have multiple query with different field under a schema. like below
Field("memberById", ListType(Member),
arguments = Id :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Field("memberByEmail", ListType(Member),
arguments = email :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Field("memberByName", ListType(Member),
arguments = name :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Thank you in advance. let me know in case you need more details.
You should think about advantanges and disadvantages of both solutions.
If you will prepare separate fields, you will get a lot of boilerplate.
On the other hand you can set all possible inputs as OptionalInputType, it makes schema field only. Disadvantage of this solutions is that Sangria cannot validate a field that at least one argument should be required, so you have to cover this case with proper response or whatever.
The third option is to make generic solution at Schema level. You can create a query with two arguments filterName and filterValues, first would be EnumType for Id, Email, Name, the second would be a list of strings.
Such solution avoid disadvantages of both previous solutions, it has required fields and it doesn't need spreading fields in schema for every filter. Additionally if you want to add any additional function you have only edit FilterName enum and a resolver function to cover this.
Finally you schema will looks like this:
enum FilterName {
ID
EMAIL
NAME
}
type Query {
member(filterName: FilterName!, filterValues: [String]!): Member!
}

Resources