Laravel lighthouse morphOne mutation - laravel

I want to allow users to upload images with their post but also have the ability to allow the users to upload images for the landingspage via a morphOne relation.
I set up my models according to the laravel docs but can provide them if needed.
than in my schema.graphql file I have the following
// schema.graphql
type Query
type Mutation
union Imageable = Blog | Landingspage
#import graphql/blog/*.graphql
#import graphql/landingspage/*.graphql
#import graphql/image/image.graphql
inside of the image.graphql file I have the following
// image.graphql
extend type Mutation {
createImage(input: ImageInput! #spread): Image #create
updateImage(input: ImageInput! #spread): Image #update
deleteImage(input: ImageInput! #spread): Image #delete
}
type Image {
id: ID!
url: String!
imageable: Imageable! #morphTo
}
input ImageInput {
id: ID!
url: String
imageable:ImageableMorphTo
}
input ImageableMorphTo {
connect: ImageableInput
disconnect: Boolean
delete: Boolean
}
input ImageableInput {
type: String!
id: ID!
}
and lastly in my blog.graphql file I have this
// blog.graphql
extend type Query {
blogs: [Blog!]! #all #orderBy(column: "created_at", direction: DESC)
blog(slug: String! #eq): Blog #find
}
extend type Mutation {
createBlog(input: CreateBlogInput #spread): Blog #create
}
type Blog {
id: ID!
title: String!
big_text: String!
small_text: String!
slug: String!
category_id: Int
created_at: DateTime!
updated_at: DateTime!
image: Image #morphOne
}
input CreateBlogInput {
title: String!
big_text: String!
small_text: String!
category_id: Int,
image: ImageInput
}
Now when I go to the graphql-playground and create the mutation
mutation ($input: CreateBlogInput ){
createBlog(input:$input){
id
title
small_text
big_text
image{
id
url
}
}
}
with the following input
{
"input": {
"title": "image-test",
"big_text": "big_text",
"small_text": "small_text",
"category_id": 2,
"image": {
"id": 3,
"url": "https://cats.example/cute"
}
}
}
my response is this
{
"data": {
"createBlog": {
"id": "7",
"title": "image-test",
"small_text": "small_text",
"big_text": "big_text",
"image": null
}
}
}
How do I make image not null anymore? I tried to reverse engineer the example at
https://lighthouse-php.com/master/eloquent/nested-mutations.html#morphto
but this only shows you how to create a image and connect a post (or blog) to it, but I want to create a post with an image.

Firstly, if you want that your image field were not null, just add a !, so:
type Blog {
# ...
image: Image! #morphOne
}
Secondly, if you want to create a Blog with an Image, the input should be like:
extend type Mutation {
createBlog(input: CreateBlogInput #spread): Blog #create
}
input CreateBlogInput {
title: String!
big_text: String!
small_text: String!
category_id: Int,
image: BlogImageRelationInput
}
input BlogImageRelationInput {
upsert: UpsertImageInput
}
input UpsertImageInput {
id: ID
url: String
}

Related

graphQL Query: getting error "Expected value of type ..., found ..."

Suppose I have the following object types:
type Price {
currency: Currency!,
amount: Float!
}
type Attribute {
displayValue: String,
value: String,
id: String!
}
type AttributeSet {
id: String!,
name: String,
type: String,
items: [Attribute]
}
type Product {
id: String!,
name: String!,
inStock: Boolean,
gallery: [String],
description: String!,
category: String!,
attributes: [AttributeSet]
prices: [Price!]!,
brand: String!
}
type Category {
name: String,
products: [Product]!
}
type Currency {
label: String!,
symbol: String!
}
input CategoryInput {
title: String!
}
type Query {
categories: [Category],
category(input: CategoryInput): Category,
product(id: String!): Product,
currencies: [Currency]
}
And these are the Types for Category:
export enum Category {
all = 'all',
clothes = 'clothes',
tech = 'tech'
};
In graphQL Playground, I am trying to make a query to exhibit all the names and products/id of the elements with the category all. Here's my attempt:
{
category(input: "all") {
name
products {
id
}
}
}
But I'm getting the following error message:
"message": "Expected value of type \"CategoryInput\", found \"all\".",
I need help trying to understand what went wrong since all is a valid type. Thank you in advance.
Just found my mistake
CategoryInput is of type
input CategoryInput {
title: String!
}
So a proper query would be:
{
category(input: { title: "all" }) {
name
products {
id
}
}
}

Graphql React Dev Tools Missing field while writing result

I am running a graphql query to create a person object. The query works fine and the person is created. However I am receiving the following error in the console
react_devtools_backend.js:2842 Missing field 'create_person' while writing result {
"__typename": "PersonResponse",
"error": null,
"person": {
"__typename": "Person",
"hire_date": "2020-10-01",
"name": {
"__typename": "Name",
"first_name": "Joe",
"last_name": "Doe",
"middle_name": ""
},
"person_id": {
"__typename": "PersonId",
"id_": "44df8f7c-d019-410c-89b4-be602f631055"
},
"preferred_name": {
"__typename": "PreferredName",
"first_name": "J",
"last_name": "Dee",
"middle_name": null
}
},
"status": 201
}
This error seems to be saying that my query is missing a field create_person however create_person is not a field it is the name of the query. My first thought was that the message is due to the null fields (even though they are not required). I tried removing these fields from the schema and I still get the error. I am using React Dev Tools chrome extension but still not sure why I get this error.
As requested the gql schema:
const graphqlSchema = buildSchema(`
type Query {
people(person_id: String): [Person]!
emergency_contacts(
person_id: String!
emergency_contact_id: String
): [EmergencyContact]!
person_success: PersonResponse!
person_not_found: PersonResponse!
person_deleted: PersonResponse!
emergency_contact_success: EmergencyContactResponse!
}
scalar Datetime
type PersonId {
id_: String!
}
type Name {
first_name: String!
last_name: String!
middle_name: String
}
input NameInput {
first_name: String!
last_name: String!
middle_name: String
}
input UpdateNameInput {
first_name: String
last_name: String
middle_name: String
}
type ExemptStatus {
code: String!
description: String!
}
type PreferredName {
first_name: String
last_name: String
middle_name: String
}
input PreferredNameInput {
first_name: String
last_name: String
middle_name: String
}
type Relationship {
code: String!
display: String!
description: String!
}
type Address {
line_1: String!
line_2: String!
city: String!
state: String!
zip_code: String!
}
type Person {
person_id: PersonId!
name: Name!
hire_date: Datetime!
preferred_name: PreferredName
contact_info: ContactInfo
exempt_status: ExemptStatus
hired_hours_per_week: Float
hired_hours_per_two_weeks: Float
emergency_contacts: [EmergencyContact]
}
type PersonResponse {
status: Int
error: String
person: Person
}
input PersonInput {
name: NameInput!
hire_date: Datetime!
preferred_name: PreferredNameInput
contact_info: ContactInfoInput
exempt_status_code: String
hired_hours_per_week: Float
hired_hours_per_two_weeks: Float
}
input AddressInput {
line_1: String!
line_2: String!
city: String!
state: String!
zip_code: String!
}
type ContactInfo {
primary_phone: String!
alternate_phone: String
email: String
address: Address
}
input ContactInfoInput {
primary_phone: String!
alternate_phone: String
email: String
address: AddressInput
}
type EmergencyContactId {
id_: String!
}
type EmergencyContact {
emergency_contact_id: EmergencyContactId!
name: Name!
relationship: Relationship!
contact_info: ContactInfo!
}
input EmergencyContactInput {
name: NameInput!
relationship_code: String!
contact_info: ContactInfoInput!
}
input UpdateEmergencyContactInput {
name: NameInput
relationship_code: String
contact_info: ContactInfoInput
}
type EmergencyContactResponse {
status: Int
error: String
person_id: PersonId
emergency_contact: EmergencyContact
}
type Mutation {
create_person(person_input: PersonInput): PersonResponse
update_name(person_id: String!, name_input: UpdateNameInput!): PersonResponse
update_hire_date(person_id: String!, hire_date: Datetime!): PersonResponse
update_preferred_name(
person_id: String!
preferred_name_input: UpdateNameInput!
): PersonResponse
delete_person(person_id: String!): PersonResponse
}
`);

"Variable '$data' expected value of type 'VoteCreateInput"

When I am trying to do the "vote" mutation, getting the below error. My other mutations are working fine.
When I am trying to do the "vote" mutation, getting the below error. My other mutations are working fine.
When I am trying to do the "vote" mutation, getting the below error. My other mutations are working fine.
"data": null,
"errors": [
{
"message": "Variable '$data' expected value of type 'VoteCreateInput!' but
got: {\"user\":{\"connect\":
{\"id\":\"ck1j3nzi68oef090830r8wd6b\"}},\"link\":{\"connect\":
{\"id\":\"ck1j58loj8x570908njwe4eu7\"}}}. Reason: 'User' Expected non-null
value, found null. (line 1, column 11):\nmutation ($data:
VoteCreateInput!) {\n ^",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"vote"
]
}
]
Mutation
async function vote(parent, args, context, info) {
// 1
const userId = getUserId(context)
// 2
const linkExists = await context.prisma.$exists.vote({
user: { id: userId },
link: { id: args.linkId },
})
if (linkExists) {
throw new Error(`Already voted for link: ${args.linkId}`)
}
// 3
return context.prisma.createVote({
user: { connect: { id: userId } },
link: { connect: { id: args.linkId } },
})
}
datamodel.schema
type Link {
id: ID! #id
createdAt: DateTime! #createdAt
description: String!
url: String!
postedBy: User
votes: [Vote!]!
}
type User {
id: ID! #id
name: String!
email: String #unique
password: String!
links: [Link!]!
votes: [Vote!]!
}
type Vote {
id: ID! #id
link: Link!
user: User!
}
schema.graphql
type Mutation {
vote(linkId: ID!): Vote!
}
type Link {
id: ID!
description: String!
url: String!
postedBy: User
votes: [Vote!]!
}
There was a glitch in prisma database which was not updating with the change in data model.
I have created a new instance of the db and now its working fine.

Querying NOT NULL GraphQL with Prisma

Schema:
type TrackUser {
id: ID! #unique
createdAt: DateTime!
user: User #note there is no `!`
}
type User {
id: ID! #unique
name: String! #unique
}
I want to get Alls TrackUser where User is not null. What would be the query?
This would be a possible query:
query c {
trackUsers(where: { NOT: [{ user: null }] }) {
name
}
}
Here you can see how it looks in the Playground. I added a name to Trackuser in the datamodel in order to be able to create it from that side without a user.
this works, but I guess it is just a hack..
query TrackUsersQuery($orderBy: TrackUserOrderByInput!, $where: TrackUserWhereInput, $first: Int, $skip: Int) {
trackUsers(where: $where, orderBy: $orderBy, first: $first, skip: $skip) {
id
createdAt
user {
id
name
}
}
}
variables = {
where: {
user: {
name_contains: ''
}
}
}
UPDATE:
For Prisma2, here you have the possibilities:
For products that have no invoice, you can use the following:
const data = await prisma.product.findMany({
where: {
invoices: {
none: {
id: undefined,
},
},
},
})
And for Invoices that do not have a product associated:
const data = await prisma.invoice.findMany({
where: {
productId: null,
},
})
more details here: https://github.com/prisma/prisma/discussions/3461

graphql, how to design input type when there are "add" and "update" mutation?

Here are my requirements:
"add" mutation, every field(or called scalar) of BookInput input type should have additional type modifiers "!" to validate the non-null value. Which means when I add a book, the argument must have title and author field, like {title: "angular", author: "novaline"}
"update" mutation, I want to update a part of fields of the book, don't want to update whole book(MongoDB document, And, I don't want front-end to pass graphql server a whole big book mutation argument for saving bandwidth). Which means the book argument can be {title: "angular"} or {title: "angular", author: "novaline"}.
Here are my type definitions:
const typeDefs = `
input BookInput {
title: String!
author: String!
}
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
type Mutation{
add(book: BookInput!): Book
update(id: String!, book: BookInput!): Book
}
`;
For now, "add" mutation works fine. But "update" mutation cannot pass the non-null check if I pass {title: "angular"} argument
Here is a mutation which does not pass the non-null check, lack of "author" field for BookInput input type.
mutation {
update(id: "1", book: {title: "angular"}) {
id
title
author
}
}
So, graphql will give me an error:
{
"errors": [
{
"message": "Field BookInput.author of required type String! was not provided.",
"locations": [
{
"line": 2,
"column": 24
}
]
}
]
}
How do I design the BookInput input type? Don't want to define addBookInput and updateBookInput. It's duplicated.
A very common pattern is to have separate input types for each mutation. You may also want to create one mutation query per operation. Perhaps something like this:
const typeDefs = `
input AddBookInput {
title: String!
author: String!
}
input UpdateBookInput {
# NOTE: all fields are optional for the update input
title: String
author: String
}
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
type Mutation{
addBook(input: AddBookInput!): Book
updateBook(id: String!, input: UpdateBookInput!): Book
}
`;
Some people also like to include the update ID as part of the update input:
const typeDefs = `
input AddBookInput {
title: String!
author: String!
}
input UpdateBookInput {
# NOTE: all fields, except the 'id' (the selector), are optional for the update input
id: String!
title: String
author: String
}
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book!]!
}
type Mutation{
addBook(input: AddBookInput!): Book
updateBook(input: UpdateBookInput!): Book
}
`;
Finally, you may want to use a 'payload' type for the return type - for added flexibility (gives you more wiggle room to change the return type later without breaking your API):
const typeDefs = `
input AddBookInput {
title: String!
author: String!
}
input UpdateBookInput {
# NOTE: all fields, except the 'id' (the selector), are optional for the update input
id: String!
title: String
author: String
}
type Book {
id: ID!
title: String!
author: String!
}
type AddBookPayload {
book: Book!
}
type UpdateBookPayload {
book: Book!
}
type Query {
books: [Book!]!
}
type Mutation{
addBook(input: AddBookInput!): AddBookPayload!
updateBook(input: UpdateBookInput!): UpdateBookPayload!
}
`;
Hope this helps!
Here is my solution, I write a helper function to generate "create" input type and "update" input type.
const { parse } = require('graphql');
/**
* schema definition helper function - dynamic generate graphql input type
*
* #author https://github.com/mrdulin
* #param {string} baseSchema
* #param {object} options
* #returns {string}
*/
function generateInputType(baseSchema, options) {
const inputTypeNames = Object.keys(options);
const schema = inputTypeNames
.map(inputTypeName => {
const { validator } = options[inputTypeName];
const validatorSchema = Object.keys(validator)
.map(field => `${field}: ${validator[field]}\n`)
.join(' ');
return `
input ${inputTypeName} {
${baseSchema}
${validatorSchema}
}
`;
})
.join(' ')
.replace(/^\s*$(?:\r\n?|\n)/gm, '');
parse(schema);
return schema;
}
schema.js:
${generateInputType(
`
campaignTemplateNme: String
`,
{
CreateCampaignTemplateInput: {
validator: {
channel: 'ChannelUnionInput!',
campaignTemplateSharedLocationIds: '[ID]!',
campaignTemplateEditableFields: '[String]!',
organizationId: 'ID!',
},
},
UpdateCampaignTemplateInput: {
validator: {
channel: 'ChannelUnionInput',
campaignTemplateSharedLocationIds: '[ID]',
campaignTemplateEditableFields: '[String]',
organizationId: 'ID',
},
},
},
)}

Resources