Error: field type must be Input Type - cannot pass qlType into mutation - reactjs

I want to update a person with the UpdatePerson mutation. I don't want to specify each and every property in the inputFields - but rather want to pass the complete person object.
When I do that I get Error: UpdatePersonInput.person field type must be Input Type but got: Person.
Is there no way to pass complete objects rather than all of their properties to a mutation?
If there isn't, could you add one - because the amount of repetition of fields across a larger app with bigger objects can become very frustrating.
Same might be an issue on getFatQuery and static fragments. Repeating all the properties over and over again would be a nightmare.
Server:
/**
* Create the GraphQL Mutation.
*/
export default mutationWithClientMutationId({
// Mutation name.
name: 'UpdatePerson',
// Fields supplied by the client.
inputFields: {
person: {type: qlPerson} // <========================================
},
// Mutated fields returned from the server.
outputFields: {
person: {
type: qlPerson,
// Parameters are payload from mutateAndGetPayload followed by outputFields.
resolve: (dbPerson, id, email) => {
return dbPerson;
}
}
},
// Take the input fields, process the mutation and return the output fields.
mutateAndGetPayload: ({qlPerson}, {rootValue}) => {
// TODO: Process Authentication {"session":{"userId":1}}
console.log(JSON.stringify(rootValue));
// Convert the client id back to a database id.
var localPersonId = fromGlobalId(qlPerson.id).id;
// Find the person with the given id in the database.
return db.person.findOne({where: {id: localPersonId}}).then((dbPerson)=> {
// Mutate the person.
dbPerson.email = qlPerson.email;
// Save it back to the database.
return dbPerson.save().then(()=> {
// Return the mutated person as an output field.
return dbPerson;
});
});
}
});
Client:
/**
* Create the GraphQL Mutation.
*/
class UpdatePersonMutation extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation {updatePerson}`;
}
getVariables() {
return {person: this.props.person}; // <========================================
}
getFatQuery() {
return Relay.QL`
fragment on UpdatePersonPayload {
person {
email, // ??????????????????????????
}
}
`;
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
person: this.props.person.id
}
}];
}
static fragments = {
person: () => Relay.QL`
fragment on Person {
id,
email // ???????????????????????????
}
`
};
getOptimisticResponse() {
return {
person: this.props.person
};
}
}
/**
* Exports.
*/
export default UpdatePersonMutation;

It's error because your qlPerson type was defined by using the GraphQLObjectType class, which is not an Input type. You must define it using the GraphQLInputObjectType instead. Basically, both of them take an object as an argument which requires same properties. So, you just need to use GraphQLInputObjectType instead of GraphQLObjectType as following:
export default new GraphQLInputObjectType({
name: 'qlPerson',
description: 'Dietary preferences',
fields: () => ({
firstName: {type: GraphQLString},
...
})
});

Related

[TypeGraphQL]Cannot determine GraphQL input type

An error occurs in the following configuration. Please tell me the cause of the error and how to fix it.
■Error message
Error: Cannot determine GraphQL input type for 'zzzzzInputs' of 'XxxxxInput' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper input value?
■Reproduction environment and method
Clone
git clone https://github.com/isoittech/hellpj-type-graphql
Execute commands
npm ci
npm run start
■Application/library stack
Node.js/Express
sequelize
sequelize-typescript
type-graphql
■Source
https://github.com/isoittech/hellpj-type-graphql/blob/master/src/main/graphql/ppppp.resolver.ts
#ObjectType()
export class ZzzzzInput {
#Field((type) => ZzzzzType)
zzzzz!: ZzzzzType;
// #Field()
// zzzzz!: string;
}
#InputType()
export class XxxxxInput {
#Field((type) => [ZzzzzInput])
zzzzzInputs!: ZzzzzInput[];
// #Field((type) => [String])
// zzzzzInputs!: string[];
}
#Resolver((of) => Yyyyy)
export class XxxxxResolver {
#Mutation((returns) => Yyyyy)
async addXxxxx(#Arg("Xxxxx") XxxxxInput: XxxxxInput): Promise<Yyyyy> {
const serviceOutput: XxxxxServiceOutputDto = {};
return Promise.resolve(serviceOutput.addedXxxxx!);
}
}
■I coded it by referring to here.
https://typegraphql.com/docs/types-and-fields.html
Cannot determine GraphQL input type for argument named
I solved my problem by modifying like below. Look at '★'.
(And found another one, solved too.)
■Source
// #ObjectType() // ★ Before
#InputType() // ★ After
export class ZzzzzInput {
#Field((type) => ZzzzzType)
zzzzz!: ZzzzzType;
// #Field()
// zzzzz!: string;
}
#InputType()
export class XxxxxInput {
#Field((type) => [ZzzzzInput])
zzzzzInputs!: ZzzzzInput[];
// #Field((type) => [String])
// zzzzzInputs!: string[];
}
#Resolver((of) => Yyyyy)
export class XxxxxResolver {
#Mutation((returns) => Yyyyy)
async addXxxxx(#Arg("Xxxxx") XxxxxInput: XxxxxInput): Promise<Yyyyy> {
const serviceOutput: XxxxxServiceOutputDto = {};
return Promise.resolve(serviceOutput.addedXxxxx!);
}
}
■Related source
node_modules/type-graphql/dist/schema/schema-generator.js
static getGraphQLInputType(target, propertyName, type, typeOptions = {}, parameterIndex, argName) {
let gqlType;
gqlType = types_1.convertTypeIfScalar(type);
if (!gqlType) {
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
★ Finding process below not worked when #ObjectType.
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
const inputType = this.inputTypesInfo.find(it => it.target === type);
if (inputType) {
gqlType = inputType.type;
}
}
if (!gqlType) {
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
☆ Finding process below not worked when wrong order in src/index.ts.
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
const enumType = this.enumTypesInfo.find(it => it.enumObj === type);
if (enumType) {
gqlType = enumType.type;
}
}
if (!gqlType) {
throw new errors_1.CannotDetermineGraphQLTypeError("input", target.name, propertyName, parameterIndex, argName);
}
const { nullableByDefault } = build_context_1.BuildContext;
return types_1.wrapWithTypeOptions(target, propertyName, gqlType, typeOptions, nullableByDefault);
}
■Another problem
After I've solved above problem, another one occured. Error message is below:
Error: Cannot determine GraphQL input type for 'zzzzz' of 'ZzzzzInput' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper input value?
Look at ☆ in schema-generator.js.
Enum type 'ZzzzzType' couldn't be found, so error occured.
Because this.enumTypesInfo doesn't contain 'ZzzzzType'.
Because I executed registering Enumtype after SchemaGenerating process.
I had to modify below.
// enable Enum
registerEnumType(ZzzzzType, {
name: "ZzzzzType",
});
const schema = await buildSchema({
resolvers: [__dirname + "/graphql/*.resolver.ts"],
emitSchemaFile: true,
validate: false,
});
// // enable Enum
// registerEnumType(ZzzzzType, {
// name: "ZzzzzType",
// });

Laravel Lighthouse, how can i get all array data when i do subscription?

I do subscription with laravel + Lighthouse + Laravel WebSockets + vue-apollo tech.
When i subscription, i wanna get all array data, but i only got changed data.
My schema.graphql is below.
type Mutation {
updateTest(id: ID!, name: String, result: Int): Test #update
#broadcast(subscription: "testSub")
}
type Subscription {
testSub(id: ID): Test
}
type Test {
id: ID!
name: String
result: Int
}
This is my vue-apollo code
const subQuery = gql`subscription testSub($id: ID) {
testSub(id:$id) {
id
name
}
}`
const observer = this.$apollo.subscribe({
query: subQuery,
variables () {
return {
id: 14,
}
},
})
observer.subscribe({
next (data) {
console.log('subscribe')
console.log(data)
},
error (error) {
console.log('err')
console.error(error)
},
})
When i do mutation like below.
mutation {
updateTest(id:14, name: "hahaha", result:1) {
id
name
}
}
vue-apollo get subscription like pic.
I recognized return is only changed value instead of all data.
So i change subscription schema like below.
type Subscription {
testSub: [Test] #all
}
I also changed vue-apollo code.
const subQuery = gql`subscription testSub { #delete argument
testSub { #delete argument
id
name
}
}`
const observer = this.$apollo.subscribe({
query: subQuery,
variables () {
return {
id: 14,
}
},
})
observer.subscribe({
next (data) {
console.log('subscribe')
console.log(data)
},
error (error) {
console.log('err')
console.error(error)
},
})
When i do mutation after npm run dev and websocket start, i got this error.
But i already made testSub.
php artisan lighthouse:subscription testSub
This is my testSub file.
<?php
namespace App\GraphQL\Subscriptions;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Subscriptions\Subscriber;
use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use App\Test as Test2;
use App\Events\test;
class TestSub extends GraphQLSubscription
{
/**
* Check if subscriber is allowed to listen to the subscription.
*
* #param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
* #param \Illuminate\Http\Request $request
* #return bool
*/
public function authorize(Subscriber $subscriber, Request $request): bool
{
// TODO implement authorize
return true;
}
/**
* Filter which subscribers should receive the subscription.
*
* #param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
* #param mixed $root
* #return bool
*/
public function filter(Subscriber $subscriber, $root): bool
{
return true;
// TODO implement filter
}
public function encodeTopic(Subscriber $subscriber, string $fieldName): string
{
// Optionally create a unique topic name based on the
// `author` argument.
//$args = $subscriber->args;
//return Str::snake($fieldName).':'.$args['author'];
//return Str::snake($fieldName).':1';
return 'testSub';
}
/**
* Decode topic name.
*
* #param string $fieldName
* #param \App\Post $root
* #return string
*/
public function decodeTopic(string $fieldName, $root): string
{
// Decode the topic name if the `encodeTopic` has been overwritten.
$author_id = $root->author_id;
//return Str::snake($fieldName).':'.$author_id;
return 'testSub';
}
public function resolve($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): Test2
{
event(new test());
return $root;
}
}
How can i get all array data instead of changed data?
In your vue-apollo code you have this:
gql`subscription testSub($id: ID) {
testSub(id:$id) {
id
name
}
}`
So, think that it is like a query. Everytime the subscription is fired, you are querying the id and name. If you want to also query the result, just add it:
gql`subscription testSub($id: ID) {
testSub(id:$id) {
id
name
result # <--
}
}`
There is no way to tell to apollo that has to fetch "all" fields of the Test type. You have to explicit those fields.

Storing Unselected users from a checkbox list (initial checked) to an array -

I have an array (checkeduserslist) which contains pairs of userid and username values. It is displayed as the image below
My requirement is that when i unselect a check box , those value is to be stored in another array, say "unselectedUsersList"
Below is a dummy funnction that im trying to implement.
unselectExistingUser(usr: any, event: any) {
if (event.target.checked == false) {
this.unselectedUsersList.push(usr);
}
else if (event.target.checked) {
var indx = this.unselectedUsersList.findIndex(usr);
this.unselectedUsersList.splice(indx, 1);
}
console.log('unselected users :', this.unselectedUsersList);
}
The argument usr contains the userid/password values that is to be inserted or deleted. Insertion is taking place in this, but no idea how to spice the value of when checked.
Thanks in advance.
I would take a different path. Instead of using two arrays I would use one single array and a flag isSelected:
interface User {
id: number;
}
interface SelectableUser extends User {
isSelected: boolean;
}
class Something {
users: SelectableUser[];
unselectExistingUser(usr: SelectableUser, event: any) {
this.users.map((currentUser) => {
if (usr.id === currentUser.id) {
currentUser.isSelected = event.target.checked;
}
return currentUser;
});;
}
}

Type 'Observable<any>' is not assignable to type 'StoresSummaryResults'. Property 'Data' is missing in type 'Observable<any>'

I am new to using the Observable with Angular 2 and my structure will not receive the results, even though I can validate the response from my REST API.
I have the following data class in Typescript.
import { RESTResult } from '../common/RESTResult'; // Common Response Structure
import { StoresSummaryData } from './StoresSummaryData'; // Structure of records being returned as an array
export class StoresSummaryResults extends RESTResult {
Data: Array<StoresSummaryData>; // Data[] is array ofStoresSummaryData structure ({ field1, field2 ...)
constructor() {
super();
this.Data = new Array<StoresSummaryData>(); // Ensure 1 records exists for testing
this.Data.push(new StoresSummaryData(0, 0));
}
}
The results are retrieved from the REST API
getResults(): Observable<StoresSummaryResults> { // Structure
return this.http.get(this.RequestHttpPath)
.map(this.extractData)
.catch(this.handleError);
};
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
StoreInfo: StoresSummaryResults;
public constructor(private storesService: StoresService) { }
showResults(): void {
this.StoreInfo = this.storesService.getResults();
}
I get the error:
Typescript Error
Type 'Observable<StoresSummaryResults>' is not assignable to type 'StoresSummaryResults'. Property 'Data' is missing in type 'Observable<StoresSummaryResults>'.
I do have the Data structure though defined, so I am not sure what to correct.
The StoreInfo property is typed as a StoresSummaryResults, but you are trying to assign it the return value of storesService.getResults() which is Observable<StoresSummaryResults>.
So either change StoreInfo to be typed as Observable<StoresSummaryResults>, or assign it in a subscription:
this.storeSevice.getResults()
.subscribe(results => this.StoreInfo = results);

No match on any of the rangeBehaviors specified in RANGE_ADD config

I have the following mutation:
export default class AddTaskMutation extends Relay.Mutation {
static fragments = {
classroom: () => Relay.QL`
fragment on Classroom {
id,
}`,
};
getMutation() {
return Relay.QL`mutation { addTask }`;
}
getFatQuery() {
return Relay.QL`
fragment on AddTaskPayload {
classroom {
taskList,
},
taskEdge,
}
`;
}
getConfigs() {
let rangeBehaviors = {};
let member_id = 'hasNewMessages(true) member_id(' + Global.fromGlobalId(this.props.createdByMember)['id'] + ')';
rangeBehaviors[''] = 'append';
rangeBehaviors['hasToDo(true)'] = 'append';
rangeBehaviors['hasToDo(false)'] = 'append';
rangeBehaviors['isStart(true)'] = 'ignore';
rangeBehaviors['isStart(false)'] = 'append';
rangeBehaviors[`${member_id}`] = 'append';
rangeBehaviors['hasNewMessages(true) member_id(null)'] = 'ignore';
return [
{
type: 'RANGE_ADD',
parentName: 'classroom',
parentID: this.props.classroomId,
connectionName: 'taskList',
edgeName: 'taskEdge',
rangeBehaviors,
}
];
}
getVariables() {
return {
title: this.props.title,
instruction: this.props.instruction,
start_date: this.props.start_date,
end_date: this.props.end_date,
is_archived: this.props.is_archived,
is_published: this.props.is_published,
is_preview: this.props.is_preview,
productId: this.props.productId,
classroomId: this.props.classroomId,
createdByMember: this.props.createdByMember,
subTasks: this.props.subTasks,
students: this.props.students,
};
}
}
When running my application I get the following 2 warnings:
warning.js:44 Warning: RelayMutation: The connection taskList{hasNewMessages:true,member_id:null} on the mutation field classroom that corresponds to the ID Classroom:35 did not match any of the rangeBehaviors specified in your RANGE_ADD config. This means that the entire connection will be refetched. Configure a range behavior for this mutation in order to fetch only the new edge and to enable optimistic mutations or use refetch to squelch this warning.
and
warning.js:44 Warning: Using null as a rangeBehavior value is deprecated. Use ignore to avoid refetching a range.
Since the other rangeBehaviors work, I assume there must be a syntactical error when declaring 2 variables in one behavior - in this case hasNewMessages and memeber_id.
I've looked for an answer for this, but I just cannot find any. The docs don't seem to cover this edge case either.
EDIT: I also tried rangeBehaviors['hasNewMessages(true),member_id(null)'] = 'ignore'; (comma as a separator) but with no success.
After inspecting the source code of Relay (file RelayMutationQuery.js) I could see what array key it was searching for in the array of all rangeBehaviors. I could then update my code to the correct formatting.
Since I haven't found anything on the web about this edge case, I'll post my solution here - perhaps someone will find it helpful in the future.
When having 2 (or more) variables for a rangeBehavior, you need to separate them with a period (.). Also, when passing null you don't pass it explicitly - just omit it from its' variable.
For example:
Right:
rangeBehaviors['hasNewMessages(true).member_id()'] = 'ignore';
Wrong:
rangeBehaviors['hasNewMessages(true),member_id(null)'] = 'ignore';

Resources