hygraph / rich-text

A set of companion packages for Hygraph's Rich Text Field
MIT License
93 stars 18 forks source link

ClientError: expected RichText raw value to be object: #120

Closed aunruh closed 9 months ago

aunruh commented 9 months ago

When transforming my html to ast, when I upsert my data to Hygraph, I get the error:

"ClientError: expected RichText raw value to be object"

The ast object I get back is:

[
  { type: 'paragraph', children: [ [Object] ] },
  { type: 'paragraph', children: [ [Object], [Object] ] },
  {
    type: 'paragraph',
    children: [ [Object], [Object], [Object], [Object], [Object] ]
  },
  { type: 'paragraph', children: [ [Object], [Object] ] },
  { type: 'paragraph', children: [ [Object], [Object] ] },
  {
    type: 'paragraph',
    children: [ [Object], [Object], [Object], [Object] ]
  },
  { type: 'paragraph', children: [ [Object], [Object] ] },
  { type: 'paragraph', children: [ [Object] ] },
  { type: 'paragraph', children: [ [Object] ] },
  { type: 'paragraph', children: [ [Object], [Object] ] },
  {
    type: 'paragraph',
    children: [ [Object], [Object], [Object], [Object] ]
  },
  { type: 'paragraph', children: [ [Object] ] }
]

This is my code. I use text_html: ${{ children: data.text_html }} to insert the ast object.


const { GraphQLClient, gql } = require('graphql-request') // Importing graphql-request library and gql tag
require('dotenv').config() // Importing environment variables from .env file via the dotenv NPM package
const { htmlToSlateAST } = require('@graphcms/html-to-slate-ast')

// Initialize GraphQL client with endpoint and authorization headers
const client = new GraphQLClient(process.env.HYGRAPH_ENDPOINT, {
    headers: {
        authorization: `Bearer ${process.env.HYGRAPH_TOKEN}`,
    },
})

// Function to create a GraphQL mutation based on provided data
// https://hygraph.com/docs/api-reference/content-api/mutations#upsert-entries
function createMutation(data) {
    console.log('data.text_html')
    console.log(data.text_html)
    // console.log('data', data)
    // Create the mutation
    // multiline string: https://stackoverflow.com/questions/45955084/passing-multi-line-string-in-a-graphql-mutation
    const mutation = gql`mutation MyMutation {
    upsertAnnouncement(
        where: { hylexid: "${data._id}" }
        upsert: {
            create: { 
                hylexid: "${data._id}"
                title: "${data.title.trim()}"
                service: "${data.service.trim()}"
                text_html: ${{ children: data.text_html }}
                image: """${JSON.stringify(data.image, null, 2)}"""
                slug: "${data._slug.trim()}"
                date: "${data.date}"
            }
            update: {
                title: "${data.title.trim()}"
                service: "${data.service.trim()}"
                text_html: ${{ children: data.text_html }}
                image: """${JSON.stringify(data.image, null, 2)}"""
                slug: "${data._slug.trim()}"
                date: "${data.date}"
            }
        }
      ) {
        id
    }
  }`
    return mutation
}

const pages = 2
// one page has 15 entries! so 5 * 15 = 75
const intervalTime = 10000
// what do i need this for?
let idsToUpdate = []
let interval
// Asynchronous function to run the migration process
async function run() {
    let i = 0

    // immediately fetch
    console.log(`Running page ${i} of ${pages}`)
    fetchData(i)
    i++

    if (i < pages) {
        // then fetch every 10 seconds
        interval = setInterval(() => {
            if (i > pages) {
                clearInterval(interval)
            }
            console.log(`Running page ${i} of ${pages}`)
            fetchData(i)
            i++
        }, intervalTime)
    }
}

async function toSlate(htmlString) {
    const ast = await htmlToSlateAST(htmlString)
    return ast
}

var fetchData = async function (i) {
    let CAT_API_URL = `https://api.xxxxxx.com/v2/announcements?from=${i}&key=${process.env.EFLUX_API_KEY}`

    let { data } = await fetch(CAT_API_URL).then((res) => res.json())

    const mutations = data.map(async (item, index) => {
        const ast = await toSlate(item.text_html)
        // console.log('ast')
        // console.log(ast)
        item.text_html = ast
        // console.log('item.text_html', item.text_html)
        return createMutation(item)
    })

    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
    Promise.all(mutations).then((mutations) => {
        // console.log('mutations')
        // console.log(mutations)

        // Execute each mutation after a timed delay
        mutations.forEach((item, index) => {
            setTimeout(() => {
                console.log(`Importing announcement ${index + 1} of ${mutations.length} of page ${i} of ${pages + 1}`)
                // Make a request to the GraphQL endpoint using the generated mutation
                client.request(item).then((response) => {
                    // console.log(response)
                    // console.log('ITEM:')
                    // console.log(JSON.stringify(item))
                    // console.log('RESPONSE:')
                    idsToUpdate.push(response.id)
                }) // Store the retrieved ID for update
            }, (index + 1) * 1000) // Delay each iteration by (index + 1) seconds
        })
    })

}

// Running the migration process
run()

This is the whole error:


ClientError: expected RichText raw value to be object: {"response":{"errors":[{"message":"expected RichText raw value to be object"}],"data":null,"extensions":{"requestId":"clrzz9gimewxb0bldfpixb6ki"},"status":400,"headers":{}},"request":{"query":"mutation MyMutation {\n    upsertAnnouncement(\n        where: { hylexid: \"581215\" }\n        upsert: {\n            create: { \n                hylexid: \"581215\"\n                title: \"Word Script Sign—The Alphabet of Art\"\n                service: \"eflux\"\n                text_html: [object Object]\n                image: \"\"\"{\n  \"thumb\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,400x400,c\",\n    \"width\": 400,\n    \"height\": 400\n  },\n  \"medium\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,680\",\n    \"width\": 680,\n    \"height\": 906\n  },\n  \"large\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,1024\",\n    \"width\": 1024,\n    \"height\": 1365\n  },\n  \"original\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg?original\",\n    \"width\": 3456,\n    \"height\": 4608\n  }\n}\"\"\"\n                slug: \"word-script-sign-the-alphabet-of-art\"\n                date: \"2024-01-30\"\n            }\n            update: {\n                title: \"Word Script Sign—The Alphabet of Art\"\n                service: \"eflux\"\n                text_html: [object Object]\n                image: \"\"\"{\n  \"thumb\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,400x400,c\",\n    \"width\": 400,\n    \"height\": 400\n  },\n  \"medium\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,680\",\n    \"width\": 680,\n    \"height\": 906\n  },\n  \"large\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,1024\",\n    \"width\": 1024,\n    \"height\": 1365\n  },\n  \"original\": {\n    \"src\": \"https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg?original\",\n    \"width\": 3456,\n    \"height\": 4608\n  }\n}\"\"\"\n                slug: \"word-script-sign-the-alphabet-of-art\"\n                date: \"2024-01-30\"\n            }\n        }\n      ) {\n        id\n    }\n  }"}}
    at makeRequest (/Users/arminunruh/Documents/git/eflux-hygraph-migration/node_modules/graphql-request/build/cjs/index.js:310:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  response: {
    errors: [ { message: 'expected RichText raw value to be object' } ],
    data: null,
    extensions: { requestId: 'clrzz9gimewxb0bldfpixb6ki' },
    status: 400,
    headers: Headers {
      [Symbol(map)]: [Object: null prototype] {
        date: [ 'Tue, 30 Jan 2024 06:28:33 GMT' ],
        'content-type': [ 'application/json' ],
        'content-length': [ '134' ],
        connection: [ 'keep-alive' ],
        'cf-ray': [ '84d7a7ca7e37727c-HAM' ],
        'cf-cache-status': [ 'DYNAMIC' ],
        'cache-control': [ 'private, no-store' ],
        vary: [ 'Origin, Accept-Encoding' ],
        'x-cdn-cache-status': [
          'optimize,skip-no-query-op,disable-cdn-not-cacheable,disable-cdn,no-transform-is-mutation,fetch-origin,reject-not-ok,update-project'
        ],
        'x-request-id': [ 'clrzz9gimewxb0bldfpixb6ki' ],
        'report-to': [
          '{"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=zvTpVM0sOeWEqBHNoy8fJSTbyvhyDhqftWa%2BvJ6lALJNWKeC%2BelelG34c6sX5fLvF6wqdQmtzhE%2FJP2c0t7rykvTmLHuoDSt9CIIrYsZGTj7PI2qUfxbzl8W9FLg439VTwmW2I7Xp0xwEfyRHHWtj7YHlsscoJW8b1saGTg0uuw090F3J8hB"}],"group":"cf-nel","max_age":604800}'
        ],
        nel: [
          '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}'
        ],
        server: [ 'cloudflare' ]
      }
    }
  },
  request: {
    query: 'mutation MyMutation {\n' +
      '    upsertAnnouncement(\n' +
      '        where: { hylexid: "581215" }\n' +
      '        upsert: {\n' +
      '            create: { \n' +
      '                hylexid: "581215"\n' +
      '                title: "Word Script Sign—The Alphabet of Art"\n' +
      '                service: "eflux"\n' +
      '                text_html: [object Object]\n' +
      '                image: """{\n' +
      '  "thumb": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,400x400,c",\n' +
      '    "width": 400,\n' +
      '    "height": 400\n' +
      '  },\n' +
      '  "medium": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,680",\n' +
      '    "width": 680,\n' +
      '    "height": 906\n' +
      '  },\n' +
      '  "large": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,1024",\n' +
      '    "width": 1024,\n' +
      '    "height": 1365\n' +
      '  },\n' +
      '  "original": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg?original",\n' +
      '    "width": 3456,\n' +
      '    "height": 4608\n' +
      '  }\n' +
      '}"""\n' +
      '                slug: "word-script-sign-the-alphabet-of-art"\n' +
      '                date: "2024-01-30"\n' +
      '            }\n' +
      '            update: {\n' +
      '                title: "Word Script Sign—The Alphabet of Art"\n' +
      '                service: "eflux"\n' +
      '                text_html: [object Object]\n' +
      '                image: """{\n' +
      '  "thumb": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,400x400,c",\n' +
      '    "width": 400,\n' +
      '    "height": 400\n' +
      '  },\n' +
      '  "medium": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,680",\n' +
      '    "width": 680,\n' +
      '    "height": 906\n' +
      '  },\n' +
      '  "large": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg,1024",\n' +
      '    "width": 1024,\n' +
      '    "height": 1365\n' +
      '  },\n' +
      '  "original": {\n' +
      '    "src": "https://images.e-flux-systems.com/581215_0e60b22986a89d2e23433b61e8d1a6bc.jpg?original",\n' +
      '    "width": 3456,\n' +
      '    "height": 4608\n' +
      '  }\n' +
      '}"""\n' +
      '                slug: "word-script-sign-the-alphabet-of-art"\n' +
      '                date: "2024-01-30"\n' +
      '            }\n' +
      '        }\n' +
      '      ) {\n' +
      '        id\n' +
      '    }\n' +
      '  }',
    variables: undefined
  }
}

my package json:


{
    "name": "xxx-hygraph-migration",
    "version": "1.0.0",
    "description": "",
    "main": "migration.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
        "@graphcms/html-to-slate-ast": "^0.13.3",
        "dotenv": "^16.4.1",
        "graphql-request": "^6.1.0",
        "html-entities": "^2.4.0",
        "jsdom": "^24.0.0",
        "slate": "^0.66.1",
        "slate-hyperscript": "^0.67.0"
    }
}
jpedroschmitz commented 9 months ago

It would be best to use variables instead of a multiline string for the query.

If you check the docs, there's a complete example on how you should do it.

import { GraphQLClient, gql } from 'graphql-request';
import { htmlToSlateAST } from '@graphcms/html-to-slate-ast';

const client = new GraphQLClient(`${process.env.HYGRAPH_ENDPOINT}`, {
  headers: {
    Authorization: `Bearer ${process.env.HYGRAPH_TOKEN}`,
  },
});

const newArticleQuery = gql`
  mutation newArticle($title: String!, $content: RichTextAST) {
    createArticle(data: { title: $title, content: $content }) {
      id
      title
      content {
        html
        raw
      }
    }
  }
`;

async function main() {
  const htmlString = '<ul><li>Hey <a href="thing">link text</a> here</li></ul>';
  const ast = await htmlToSlateAST(htmlString);

  // Create a RichText object from the AST
  const content = {
    children: ast,
  };

  const data = await client.request(newArticleQuery, {
    title: 'Example title for an article',
    content, // Pass the RichText object as the content
  });

  console.log(data);
}

main()
  .then(() => process.exit(0))
  .catch(e => console.error(e));

Please notice the "Create a RichText object from the AST" part. That's how you should be doing it.

You got the error because the "ast object" you got back is not an object, it's an array and that's what you're sending. Instead, you should create an object like this:

const content = {
  children: astArray,
};

And then send the content to the mutation.