Error in mutation nuwave/lighthouse:^5.0 and input - graphql

I am using nuwave/lighthouse:^5.0, and I am trying to create a mutation for an entity which have a belongsTo relationship. The thing is that in my input I am using a sanitizer directive to transform from string to id, but after that when Laravel gets the properties, it shows errors with the validation of the class. In addition, I debug the directive code and it works correctly.
Error
"errors": [
{
"message": "The given data was invalid.",
"extensions": {
"validation": {
"content_type_id": [
"The content type id field is required."
]
},
"category": "validation"
},
Input
input CreateContentInput {
content_type: CreateContentTypeBelongsTo!
.....
input CreateContentTypeBelongsTo {
connect: ID! #typeuuid(model: "App\\ContentType")
create: CreateContentTypeInput
update: UpdateContentTypeInput
}
Model
class Content extends Model
{
protected $rules = [
'content_type_id' => 'required|integer|is_main_content_type',
];
/**
* #return BelongsTo
*/
public function contentType(): BelongsTo
{
return $this->belongsTo(ContentType::class);
}
Any idea will be appreciated

Finally, after some days, I found the issue.
The error come from the main Input definition:
input CreateContentInput {
content_type: CreateContentTypeBelongsTo!
}
I was following a company standard that says that we need to use the properties always in ** snake case ** although they are relationships. So looks like Lighthouse uses always ** camel case ** for relationships.
The solution was add the ** rename ** property to the input. So the right input should be:
input CreateContentInput {
content_type: CreateContentTypeBelongsTo! #rename (attribute: "contentType")
}
I hope this could help someone else.

Related

Type of return data in API platform

I have a problem of changing types of returned data in API platform:
I have an entity:
final class ModelClass
{
/**
* #var float
*/
public $total;
}
And a configuration:
ModelClass:
properties:
total:
attributes:
swagger_context:
type: float
And Controller:
public function __invoke(CustomRequest $request): Paginator
{
return $this->service->getTotals($request);
}
The return of this is Paginator, which holds custom doctrine query, result of which looks like this:
{
"#type": "hydra:Collection",
"hydra:member": [
{
"id": 1,
"total": "120.00",
},
]
}
As you see, total is a string (because in the result of query it is a string). What i want it to be: a float: "total": 120.00. And what i would also like to be able to do, is to format it differently, for example separator sign ',' instead '.'
I didnt find in documentation how to do it. Is it a missing documentation or missing feature?
I would expect that API platform reads DocBlock to understand the type of Model. And i think that there is some intercept mechanism after query is executed, but before response is sent back to client, so i could change format/type.
Thank you.

Nested mutations don't seem to work in Lighthouse 3.7

I'm trying to setup a schema in Lighthouse 3.7/Laravel 5.8
What I want to achieve is this:
A User should be able to create Clists.
There is a One to Many relation between User and Clist.
And I'm trying to implement nested mutations as described here.
I have already implemented the "query" part and it works fine.
But when I test a createClist mutation in GraphQL Playground, I get this error:
"debugMessage": "Array to string conversion",
"message": "Internal server error",
"extensions": {
"category": "internal"
},
...
And I can't figure out what I'm doing wrong.
Here is my code:
type Mutation {
createClist(input: CreateClistInput! #spread): Clist #create
}
input CreateClistInput {
name: String!
description: String
starred: Boolean
user: CreateUserRelation!
ctags: CreateCtagRelation
}
input CreateUserRelation {
connect: ID!
}
input CreateCtagRelation {
create: [CreateCtagInput!]
connect: [ID!]
sync: [ID!]
}
input CreateCtagInput {
name: String!
}
And here is a screenshot of GraphQL Playground:
When using the #spread directive a typehint on the relationship in your model is required.
Taken from the docs there is the following example:
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Post extends Model
{
// WORKS
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
// DOES NOT WORK
public function comments()
{
return $this->hasMany(Comment::class);
}
}
Lighthouse uses the type hint to determine how it should handle the relationship.

createUser update related field - understanding relation

I need to set a related field's value on create, is this possible?
Details:
I have a User model with fields: email, displayname.
I have a Verify model with fields: code, action.
I created a relation between the two models like this:
I want to createUser and set the related fields of code and action at the same time. I tried this:
mutation {
createUser
(
email:"noit#mail.com",
displayname:"noit",
password:"123",
code: "this is a code",
action: "REGISTER"
) {
id
}
}
This fails with:
{
"data": null,
"errors": [
{
"message": "Unknown argument 'code' on field 'createUser' of type 'Mutation'. (line 2, column 76):\n createUser(email: \"noit#mail.com\", displayname: \"noit\", password: \"123\", code: \"this is a code\", action: \"REGISTER\") {\n ^",
"locations": [
{
"line": 2,
"column": 76
}
]
},
{
"message": "Unknown argument 'action' on field 'createUser' of type 'Mutation'. (line 2, column 100):\n createUser(email: \"noit#mail.com\", displayname: \"noit\", password: \"123\", code: \"this is a code\", action: \"REGISTER\") {\n ^",
"locations": [
{
"line": 2,
"column": 100
}
]
}
]
}
We specifically designed the Graphcool API to handle cases like this as simple as possible, you can do it like this:
mutation {
createUser (
email:"noit#mail.com",
displayname:"noit",
password:"123",
blahVerify: {
code: "this is a code",
action: "REGISTER"
}) {
id
blahVerify {
id
}
}
}
Note the nested blahVerify object argument.
This answer to a similar question goes a bit more into detail and also shows how you can use GraphQL variables to send nested mutations from Apollo Client.
As a sidenote, depending on the different possible value for the action of a Verify node, you might want to use an enum field rather than strings. You can read more about enum fields in the documentation.
You can do this on scaphold.io. The Logic system includes more than just mutation callbacks. You can fire functions before mutations to validate/clean input before it is saved to the DB, after to manage connections like this that will get returned in that same mutation payload, and asynchronously (like mutation callbacks) for kicking off long standing tasks. You can even compose functions together to pass meta-data through a chain of function invocations.

Change User default validation in Loopback

I'm developping a Loopback application extending base User model to UserCode model where each user is identified by an email plus a code fields.
So that a user can register with the same email twice but with different code.
I've seen that in node_modules/loopback/common/models/user.js at line 691 there is:
UserModel.validatesUniquenessOf('email', {message: 'Email already exists'});
I want to delete this restriction/validation but without change loopback code, of course.
How can I do it?
Maybe in the boot script I can loop through all validation and delete this one?
Figured it out
In this case you need to remove the default validations set by the User model
common/models/userCode.js
module.exports = function(UserCode){
//Add this line and it will start receiving multiple email.
delete UserCode.validations.email;
}
Also you can play with the required:true|false property to make any default defined property required or not.
common/models/userCode.json
{
"name": "UserCode",
"base": "User",
"idInjection": true,
"properties": {
"password": {
"type": "string",
"required": true
},
....
....
}
The following code the accepted answer will remove ALL the email validations:
module.exports = function(UserCode){
//Add this line and it will start receiving multiple email.
delete UserCode.validations.email;
}
Instead be selective and do something like this:
module.exports = function(UserCode){
// remove ONLY email uniqueness validation
UserCode.validations.email = UserCode.validations.email.reduce((all, one) => {
if (one.validation !== 'uniqueness') {
all.push(one);
}
return all;
}, []);
}

Structuring GraphQL types

I've run into an issue while trying to extend my API to include a GraphQL endpoint. The application I'm working on is a kind of forum with Messages. A message can contain comments of type Message. If a message is a comment it has a parent of type Message. Simplified, the schema looks like this:
type Message {
id: String
content: String
comments: [Message]
parent: Message
}
type RootQuery {
message(id: String): Message
messages: [Message]
}
The problem with this schema is that it allows for queries like this:
{
messages {
comments {
parent {
comments {
parent {
comments {
parent {
id
content
}
}
}
}
}
}
}
}
Keep in mind that I may want to allow for arbitrarily deep nesting of comments. In that case the following query should be allowed:
{
messages {
comments {
comments {
comments {
id
content
}
}
}
}
}
So, my question is this: Should I introduce a new type - Comment - to the API that do not know of its parent? Or are there any other ways of restricting this kind of unwanted behaviour?
Also, would the use of a Comment-type prohibit me from using the fragment messageFields on Message syntax in my queries? Perhaps this is the time to introduce interfaces to the schema?
Suggestion to a solution if I introduce the type Comment (I have not tried this):
interface Message {
id: String
content: String
comments: [Message]
}
type DefaultMessage : Message {
id: String
content: String
comments: [Comment]
parent: Message
}
type Comment : Message {
id: String
content: String
comments: [Message]
}
type RootQuery {
message(id: String): Message
messages: [Message]
}
Just in case anyone else ends up here wondering how to do recursive types in graphql-js, there's a useful hint in graphql-js's code:
* When two types need to refer to each other, or a type needs to refer to
* itself in a field, you can use a function expression (aka a closure or a
* thunk) to supply the fields lazily.
*
* Example:
*
* var PersonType = new GraphQLObjectType({
* name: 'Person',
* fields: () => ({
* name: { type: GraphQLString },
* bestFriend: { type: PersonType },
* })
* });
*
*/
https://github.com/graphql/graphql-js/blob/master/src/type/definition.js#L274
If a message is a comment it has a parent of type Message.
Looks like the parent field should be under type Comment, not DefaultMessage. That still wouldn't prevent a parent - comments - parent query but if you're worried about this for a DDOS reason, there are many other types of requests that are difficult to compute, even with REST APIs, and you should have other measures to detect such an attack.
Recursive nodes
However, you pose a very interesting question with the nested comments. How would you know how many times you need to nest the comment in the query to get all nested responses? I don't think it's currently possible with GraphQL to specify recursive objects.
I'd probably go around this limitation by fetching each nested comment one by one (or by X levels at a time) starting from the last comment as the node
{
messages {
comments {
id
content
}
}
}
followed by
{
node(commendId) {
comment {
id
content
}
}
}
I suppose you have a depth attribute of the Comment data structure, which should be pretty useful, for example, to limit the max nested depth when the users are posting comments.
So that your problem could be solved like this: in the resolver of the comments attribute, check the depth, return nothing if the depth is going illegal, otherwise fetch the comments and return.

Resources