Using graphql playground to test Saleor productCreate, what's the correct syntax for adding a product description? - syntax

In the code example below, if I exclude the description field the product is created successfully. With the description field in place I get a GraphQL error.
The code:
productCreate(
input: {
category: "Q2F0ZWdvcnk6MQ==", # Category ID
name: "Delete Me!", # Product name
productType: "UHJvZHVjdFR5cGU6MQ==", # Product Type ID
chargeTaxes: true,
weight: "0.3", # in Kg
rating: 5,
description: {text:"some text"}, # nope
}
)
The error:
graphql.error.base.GraphQLError: Argument \"input\" has invalid value {category: \"Q2F0ZWdvcnk6MQ==\", name: \"Delete Me!\", productType: \"UHJvZHVjdFR5cGU6MQ==\", chargeTaxes: true, weight: \"0.3\", rating: 5, description: {text: \"some text\"}}.",
"In field \"description\": Expected type \"JSONString\", found {text: \"some text\"}."

It is a string, for rich text it is using https://editorjs.io/
You can inspect the network tab in the dashboard to learn how APIs are being used

JSON string means providing a JSON text converted to a string. This can be achieved by escaping quotation marks within the JSON.
For example, this JSON
{ "text": "some text" }
can be converted to String as below:
"{\"text\":\"sometext\"}"
As you notice that the text encapsulated inside quotation marks, to be a valid String.
You can use https://jsontostring.com/ for the conversion
Your final code should be like this:
mutation {
productCreate(
input: {
category: "Q2F0ZWdvcnk6MQ==" # Category ID
name: "Delete Me!" # Product name
productType: "UHJvZHVjdFR5cGU6MQ==" # Product Type ID
chargeTaxes: true
weight: "0.3" # in Kg
rating: 5
description: "{\"text\":\"sometext\"}" # nope
}
){
product{
id
}
}
}

Sorting out the description syntax wasn't straightforward. From my question here:
Saleor on Github
I got this answer:
{
"id": "UHJvZHVjdDo3Mg==",
"description": "{\"blocks\":[{\"type\":\"paragraph\",\"data\":{\"text\":\"New description\"}}]}"
}
which I then implemented like this:
query = gql(
"""
mutation (
$slug: String!,
$product_title: String!,
$description: JSONString!,
$weight_grams: WeightScalar!,
)
{
productCreate(
input:{
category: "Q2F0ZWdvcnk6NQ==",
name: $product_title,
productType: "UHJvZHVjdFR5cGU6MQ==",
slug: $slug,
description: $description,
weight: $weight_grams,
}
)
{
errors {
field
message
}
product {
id
name
productType {
id
}
slug
}
}
}
"""
)
params = {
"product_title": str(product_title),
"description": '{"blocks":[{"type":"paragraph","data":{"text":"'
+ str(product_title + " (" + csv_product_id + ")")
+ '"}}]}',
"slug": str(csv_sku_code),
"weight_grams": str(weight_grams),
}
result = client.execute(query, variable_values=params)
This works well for us.

Related

Prisma2 Error: Invalid `prisma.post.create()` invocation: Unknown arg `tags` in data.tags for type PostUncheckedCreateInput

I want to create a post with a list of tags attached to it. The models are connected many-to-many (one post can have several tags, and one tag can have several posts in it).
Here are my prisma models:
model Post {
id String #id #default(cuid())
slug String #unique
title String
body String
tags Tag[]
}
model Tag {
id String #id #default(cuid())
posts Post[]
name String
slug String #unique
}
And here's a mutation where I'm trying to create a post, and attach tags to it:
t.field('createPost', {
type: 'Post',
args: {
title: nonNull(stringArg()),
body: stringArg(),
tags: list(arg({ type: 'TagInput' }))
},
resolve: async (_, args, context: Context) => {
// Create tags if they don't exist
const tags = await Promise.all(
args.tags.map((tag) =>
context.prisma.tag.upsert({
create: omit(tag, "id"),
update: tag,
where: { id: tag.id || "" },
})
)
)
return context.prisma.post.create({
data: {
title: args.title,
body: args.body,
slug: `${slugify(args.title)}-${cuid()}`,
// Trying to connect a post to an already existing tag
// Without the "tags: {...} everything works
tags: {
set: [{id:"ckql6n0i40000of9yzi6d8bv5"}]
},
authorId: getUserId(context),
published: true, // make it false once Edit post works.
},
})
},
})
This doesn't seem to be working.
I'm getting an error:
Invalid `prisma.post.create()` invocation:
{
data: {
title: 'Post with tags',
body: 'Post with tags body',
slug: 'Post-with-tags-ckql7jy850003uz9y8xri51zf',
tags: {
connect: [
{
id: 'ckql6n0i40000of9yzi6d8bv5'
}
]
},
}
}
Unknown arg `tags` in data.tags for type PostUncheckedCreateInput. Available args:
type PostUncheckedCreateInput {
id?: String
title: String
body: String
slug: String
}
It seems like the tags field on the post is missing? But I did run prisma generate and prisma migrate. Also I can successfully query tags on a post if I add them manually using Prisma Studio. What could be causing this issue?
You need to use connect for the author as well. So the following will work fine:
return context.prisma.post.create({
data: {
title: args.title,
body: args.body,
slug: `${slugify(args.title)}-${cuid()}`,
// Trying to connect a post to an already existing tag
// Without the "tags: {...} everything works
tags: {
set: [{id:"ckql6n0i40000of9yzi6d8bv5"}]
},
author: { connect: { id: getUserId(context) } },
published: true, // make it false once Edit post works.
},
})
In my case, the issue arose when I created a new field on the prisma model called uid and tried to run the command prisma migrate dev
It brought the error
Error:
⚠️ We found changes that cannot be executed:
• Step 0 Added the required column `uid` to the `Transactions` table without a default value. There are 1 rows in this table, it is not possible to execute this step.
You can use prisma migrate dev --create-only to create the migration file, and manually modify it to address the underlying issue(s).
Then run prisma migrate dev to apply it and verify it works.
I solved it by adding the #default("") to it.
model Transactions {
id Int #id #default(autoincrement())
uid String #default("")
account String
description String
category String
reference String
currency String #default("GBP")
amount String
status String
transactionDate String
createdAt String
updatedAt String
}

Using ID in GraphQL parametrized query

I have the following schema:
type Post {
id: ID!
text: String
}
I am using autogenerated mutations from neo4j-graphql.js, so I have access to the following mutation:
UpdatePost(
id: ID!
text: String
): Post
The issue:
When I'm using the following query:
mutation update($id: String, $text: String) {
UpdatePost(id: $id, text: $text) {
id
text
}
}
With the following parameters:
{
"id": "a19289b3-a191-46e2-9912-5a3d1b067cb2",
"text": "text"
}
I get the following error:
{
"error": {
"errors": [
{
"message": "Variable \"$id\" of type \"String\" used in position expecting type \"ID!\".",
"locations": [
{
"line": 1,
"column": 17
},
{
"line": 2,
"column": 18
}
],
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED"
}
}
]
}
}
Is there a way to convert my string ID to the actual ID type? Or circumvent this error altogether?
The error you're seeing is related to the type of your variables as specified in your query's variable definitions. It has nothing to do with the values of the variables (which appear to be correct). Based on the error message, the type of your $id variable is String, not ID as shown in the query you pasted.
Regardless, because the type of the id argument (where the $id variable is being used) is ID!, not ID, then your variable's type should also be ID!.
The ! indicates a non-null (required) type. In other words, the id argument must always be provided and it cannot be given the value null. Any variable passed the argument must also be non-null. If the variable's type is ID instead of ID!, we're telling GraphQL the variable might be omitted or have a null value, which would be incompatible with the argument where it's being used.
Note that the opposite is not true: if the argument's type was ID, then either an ID or ID! variable would be valid.
For anyone else encountering this issue, in the mutation definition you have:
mutation update($id: String, $text: String) {
UpdatePost(id: $id, text: $text) {
id
text
}
}
where you're explicitly saying the $id is a String, you need to change it to ID like so:
mutation update($id: ID, $text: String) {
UpdatePost(id: $id, text: $text) {
id
text
}
}
This is why you're seeing the error, because your query to update it's saying explicitly that ID is a type String hence the error.
In case someone else is looking for an answer, here's few example how to use ID variable in C#:
var request = new GraphQLRequest
{
Query = #"
query getToken($tokenId: ID!){{
tokens(where: {{ id: $tokenId}} orderBy: decimals, orderDirection: desc) {{
id
symbol
name
}}
}}", Variables = new
{
tokenId = "<stringValue>"
}
};
And for using lists:
query getToken($tokenIds: [ID!]){{
tokens(where: {{ id_in: $tokenIds}} orderBy: decimals, orderDirection: desc) {{
id
symbol
name
}}
}}", Variables = new
{
tokenIds = new ArrayList(<yourStringArrayListValue>)
}

Problem to structure property of an object using [apollo / graphql]

Problem
Hello friends,
I am working on an api using Apollo Server.
I am having the problem of how to display the nextEpisodeDate property only once. My solution shows nextEpisodeDate in all sub-array in the episodes property and it shouldn't be like that.
I hope someone can help me !
JSON Example
"episodes": [
{
"nextEpisodeDate": "2020-01-17"
},
{
"episode": 3,
"id": "53789/dorohedoro-3",
"imagePreview": "https://cdn.animeflv.net/screenshots/3274/3/th_3.jpg"
},
{
"episode": 2,
"id": "53755/dorohedoro-2",
"imagePreview": "https://cdn.animeflv.net/screenshots/3274/2/th_3.jpg"
},
{
"episode": 1,
"id": "53705/dorohedoro-1",
"imagePreview": "https://cdn.animeflv.net/screenshots/3274/1/th_3.jpg"
}
]
typeDefs
const resolvers = require('./resolvers');
const {gql} = require('apollo-server');
const typeDefs = gql `
extend type Query{
latest_anime: [Animes]
}
type Animes{
title: String
poster: String
synopsis: String
debut: String
type: String
rating: String
genres: [String]
episodes: [Episodes]
}
type Episodes{
nextEpisodeDate: String
episode: String
id: String
imagePreview: String
}
`
module.exports = {
typeDefs,
resolvers
};
Apollo Playground
query{
latest_anime{
title
poster
synopsis
debut
type
rating
genres
episodes{
nextEpisodeDate
episode
id
imagePreview
}
}
}
Apollo Playground Output
{
"data": {
"latest_anime": [
{
"title": "Tsugumomo OVA",
"poster": "https://animeflv.net/uploads/animes/covers/3275.jpg",
"synopsis": "OVA 4.6Kazuya Kagami nunca va a ningún lado sin su preciada “Sakura Obi” que su madre le regaló. Un día, una hermosa chica vestida con un kimono llamada Kiriha aparece ante él. Naturalmente, ella comienza a vivir en su habitación. ¿Naturalmente? ¡Esto solo es el inicio de la embarazosa y confusa...",
"debut": null,
"type": "OVA",
"rating": "4.6",
"genres": [
"accion",
"comedia",
"ecchi",
"escolares",
"seinen",
"sobrenatural"
],
"episodes": [
{
"nextEpisodeDate": null,
"episode": null,
"id": null,
"imagePreview": null
},
{
"nextEpisodeDate": null,
"episode": "1",
"id": "53753/tsugumomo-ova-1",
"imagePreview": "https://cdn.animeflv.net/screenshots/3275/1/th_3.jpg"
}
]
},
]
}
}
The only way you can get the desired response structure is to have two separate types. A field must have exactly one type, but you can use an abstract type like a union or interface in order to have each individual item in the list resolve to one of multiple types at runtime.
type AiredEpisode implements Episode {
id: String
episode: String
imagePreview: String
}
type UpcomingEpisode implements Episode {
id: String
nextEpisodeDate: String
}
interface Episode {
id: String
}
type Anime {
episodes: [Episode]
# other fields
}
You would then query the episodes like this:
query {
latest_anime {
episodes {
# fields on the interface itself like id are common to all
# implementing types so they don't need to be inside a fragment
id
# fields specific to one of the types need to be inside a fragment
... on UpcomingEpisode {
nextEpisodeDate
}
... on AiredEpisode {
id
episode
imagePreview
}
}
}
}
Side note: if your API doesn't return an id for the upcoming episodes, you should still provide one (you could use the show's id, for example, you just want to make sure it's unique). This will ensure that you don't run into caching issues if you use a client like Apollo on the front end.

Array of Objects Apollo Server and post from react

So i'm trying to figure out how to pass an array of objects from a POST request to apollo server on AWS lambda.
I've checked this out but it's not the same problem
Array of objects convert into object of objects when I use Apollo
The post request looks like this...
api.post('query', { query : `mutation {saveNewItem(description: "${description}", specials: ${JSON.stringify(specials)}){name}}`})
// comment to get rid of silly scroll bar overlapping code
Schema looks like this...
const { gql } = require('apollo-server-lambda')
const typeDefs = gql`
type ShoppingItem {
description: String
specials: [Specials]
}
input Specials {
description: String
price: String
qty: String
saved: String
}
type Mutation {
saveNewItem(description: String!, specials: [Specials]) : ShoppingItem
}
`
example Specials looks like this...
[{ // Object One
description: "First One"
price: "1.00"
qty: "1"
saved: "false"
},{ // Object two
description: "Second One"
price: "1.00"
qty: "1"
saved: "false"
}]
The error I get currently is...
'Error: The type of ShoppingItem.specials must be Output Type but got: [Specials].',
'at assertValidSchema (/Users/me/Desktop/Projects/app/build/node_modules/graphql/type/validate.js:71:11)',
If I change it to a normal "type" it complains about it not being Input type.
I've also been through the apollo server docs and can't quite see what I'm doing wrong?
Please that as mentioned by Daniel in comments whilst technically the "duplicate" answer given is correct the information offered here is far more high quality and useful to people facing the problem(in my opinion)
You can only use input types for input (GraphQLInputObjectType) and object types for output (GraphQLObjectType). You are using Specials as both: As output type for the field specials in ShoppingItem and as input type in mutation argument specials. To do this you need two types. The reason for this is that output types (can) have resolvers (this is actually abstracted away from apollo server in your case). You will have to create two different types:
type ShoppingItem {
description: String
specials: [Specials]
}
type Specials {
description: String
price: String
qty: String
saved: String
}
input SpecialsDraft {
description: String
price: String
qty: String
saved: String
}
type Mutation {
saveNewItem(description: String!, specials: [SpecialsDraft]) : ShoppingItem
}

Concatenate two fields into one field in graphql query

Let say I have the following schema
type Human {
Title: String
Name: String
}
Now the query
{
Human {
Title
Name
}
}
Returns
{
"data" {
Title: "Mr.",
Name: "Nielsen"
}
}
How do I get combined/concatenated string "Mr. Nielsen" as a result?
You would add a new field to Human, something like FullName: String and the resolve on that field would be along the lines of:
(parent) => `${parent.Title} ${parent.Name}`;

Resources