gatsby-source-wordpress resolving ACF "Url" field inconsistently depending on the field's content

Created on 18 Jan 2020  Â·  2Comments  Â·  Source: gatsbyjs/gatsby

Description

I have a large WordPress site that uses the _ACF to REST API_ plugin and _gatsby-source-wordpress_. Some post types have an ACF Url field for additional links. These links may be to files hosted on the website itself (from within the WordPress media library), or to external documents. The issue is that gatsby-source-wordpress resolves these url fields differently, depending on whether or not the url is for internal or external media. If external, the url resolves to a string, which is the desired (expected?) outcome. If internal, I instead get back a WordpressWpMedia node, with its respective fields. In any given scenario, I may have any number of external & internal urls, and the default Gatsby behavior is such that only one of these two node types can be included for a given query (e.g., I either get back the media node and all the external links are resolved to null, or I get back String and all the internal links are resolved to null.) Snippets below.

@LekoArts has noted these warnings elsewhere, but adding this here for posterity. For context, "link" is the slug for my Url fields.

warn Multiple node fields resolve to the same GraphQL field `wordpress__wp_infographics.acf.link` - [`link___NODE`, `link`]. Gatsby will use `link___NODE`.
warn Multiple node fields resolve to the same GraphQL field `wordpress__wp_annual_reports.acf.link` - [`link___NODE`, `link`]. Gatsby will use `link___NODE`.
warn Multiple node fields resolve to the same GraphQL field `wordpress__wp_media.guid` - [`guid___NODE`, `guid`]. Gatsby will use `guid___NODE`.
warn Multiple node fields resolve to the same GraphQL field `wordpress__acf_infographics.acf.link` - [`link___NODE`, `link`]. Gatsby will use `link___NODE`.
warn Multiple node fields resolve to the same GraphQL field `wordpress__acf_annual_reports.acf.link` - [`link___NODE`, `link`]. Gatsby will use `link___NODE`.
warn There are conflicting field types in your data.

If you have explicitly defined a type for those fields, you can safely ignore this warning message.
Otherwise, Gatsby will omit those fields from the GraphQL schema.

This query returns two posts, one with an internal url and one with an external url. The internal url is the one being returned, and the external url is returned as null. In this case, I need to drill into the "link" node to get the actual url string. (For clarity, the WP website in question lives at afpadev.wpengine.com.)

{
  "data": {
    "allWordpressWpInfographics": {
      "edges": [
        {
          "node": {
            "id": "54e2328d-c3db-5d1c-b711-56635156b13d",
            "title": "Access to Balanced Pain Management",
            "acf": {
              "link": {
                "link": "https://afpadev.wpengine.com/afpa-access-to-balanced-pain-management/"
              }
            }
          }
        },
        {
          "node": {
            "id": "4c95ac11-98e6-5fae-af12-0c983f08f661",
            "title": "Clinical Trials Expenses Shouldn’t Deter Participants",
            "acf": {
              "link": null
            }
          }
        }
      ]
    }
  }
}

A second query, where the returned values are the external urls, and the internal urls are returned as null. In this case, the string populates the top level field. How Gatsby determines which node type to resolve in any given query is beyond me.

{
  "data": {
    "upcoming": {
      "edges": [
        {
          "node": {
            "id": "3467adbf-05dc-56ad-80aa-d31afd43dfa0",
            "title": "National Survey of Patients’ Attitudes Toward Gout",
            "acf": {
              "link": null
            }
          }
        },
        {
          "node": {
            "id": "75a6355f-e92a-5614-9848-f4b79dcf6738",
            "title": "A Study of the Qualitative Impact of Non-Medical Switching",
            "acf": {
              "link": null
            }
          }
        },
        {
          "node": {
            "id": "91cb3282-a196-5db9-94ed-92903b181df6",
            "title": "Shared Decision-Making: Insights from Rheumatoid Arthritis Patients in Europe",
            "acf": {
              "link": "https://gafpa.org/wp-content/uploads/EAfPA_RA-Questionnaire-Findings-Report_May-2019.pdf"
            }
          }
        },
        {
          "node": {
            "id": "e6c8f355-5b46-58b3-a124-349d7238c9a7",
            "title": "Patients Indicate More Cardiovascular Education Needed",
            "acf": {
              "link": null
            }
          }
        }
      ]
    }
  }
}

Steps to reproduce

You can find my full repo here. I can create a min repro repo if requested. Alternatively, you can hit my WP install with _gatsby-source-wordpress_ and WP_ENV=afpadev.wpengine.com. The queries used for the above snippets:

query InfographicsTest {
  allWordpressWpInfographics {
    edges {
      node {
        id
        title
        acf {
          link {
            link
          }
        }
      }
    }
  }
}
query SurveysQuery {
  upcoming: allWordpressWpSurveys(sort: {fields: acf___date, order: ASC}) {
    edges {
      node {
        id
        title
        acf {
          link
        }
      }
    }
  }
}

Expected result

The ACF Url field should be resolved consistently & predictably, regardless of field content.

Actual result

The ACF Url field is resolved inconsistently, with different nodes being returned based on the field's content.

Environment

  System:
    OS: Linux 4.4 Ubuntu 18.04.3 LTS (Bionic Beaver)
    CPU: (8) x64 Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
    Shell: 5.4.2 - /usr/bin/zsh
  Binaries:
    Node: 12.14.1 - ~/.nvm/versions/node/v12.14.1/bin/node
    Yarn: 1.19.1 - /usr/bin/yarn
    npm: 6.13.4 - ~/.nvm/versions/node/v12.14.1/bin/npm
  Languages:
    Python: 2.7.15+ - /home/avi/.pyenv/shims/python
  npmPackages:
    gatsby: ^2.18.22 => 2.18.25
    gatsby-background-image: ^0.8.19 => 0.8.19
    gatsby-cli: ^2.8.27 => 2.8.27
    gatsby-gravityforms-component: ^1.0.9 => 1.0.9
    gatsby-image: ^2.2.38 => 2.2.39
    gatsby-plugin-manifest: ^2.2.37 => 2.2.37
    gatsby-plugin-offline: ^2.2.10 => 2.2.10
    gatsby-plugin-postcss: ^2.1.19 => 2.1.19
    gatsby-plugin-purgecss: ^4.0.1 => 4.0.1
    gatsby-plugin-react-helmet: ^3.1.21 => 3.1.21
    gatsby-plugin-sharp: ^2.3.13 => 2.3.13
    gatsby-plugin-stylus-resources: ^1.0.25 => 1.0.25
    gatsby-plugin-tslint: 0.0.2 => 0.0.2
    gatsby-plugin-typescript: ^2.1.26 => 2.1.26
    gatsby-source-filesystem: ^2.1.46 => 2.1.46
    gatsby-source-gravityforms: ^1.0.15 => 1.0.15
    gatsby-source-wordpress: ^3.1.58 => 3.1.58
    gatsby-transformer-sharp: ^2.3.12 => 2.3.13
WordPress question or discussion

Most helpful comment

Thanks for the response Tyler, I feared this was the case. For posterity, I took Tyler's advice and wrote a custom normalizer that added a new textLink field (populated with the url string) to all custom post types that were experiencing this problem. The url string is conditionally assigned to textLink based on the node type. Here is my custom normalizer object, in full:

{
    // Normalizes the link format, so Gatsby doesn't drop
        // one type (link___NODE or external url string)
    name: `AcfLinkNormalizer`,
    normalizer: ({ entities }) => {
        const media = entities.filter(
            e => e.__type === `wordpress__wp_media`
        )
        return entities.map(e => {
            if (
                e.__type === 'wordpress__wp_surveys' ||
                e.__type === 'wordpress__wp_infographics' ||
                e.__type === 'wordpress__wp_annual_reports'
            ) {
                if (e.acf.link___NODE)
                    e.acf.textLink = media.find(
                        m => m.id === e.acf.link___NODE
                    ).source_url
                else e.acf.textLink = e.acf.link
            }
            return e
        })
    },
}

All 2 comments

Hi @avinoamsn , this is one of the big problems with using GraphQL inference and is one of the reasons we're moving from using the REST api to using WPGraphQL. Gatsby doesn't know what the types are when using the REST api, so it needs to recursively look through the fields in your data and use inference to determine which type the field should be. In GraphQL, fields need to be a single type, but in this case the data is returning multiple types so Gatsby is unable to resolve the data properly.
In order to resolve this problem you'll need to use schema customization to pick which type it should be as the field can't be both types.
You could also use a custom normalizer to check your fields as data is fetched from WordPress and rename your field to externalLink for external links to get around this problem.
If you're not too far along in your project it might also be worth using gatsby-source-graphql and WPGraphQL, as you wont run into these issues if you go that route. The upgrade path from gatsby-source-graphql/WPGQL to the upcoming gatsby-source-wordpress@v4 will also be simpler than the upgrade from gatsby-source-wordpress@v3 to v4.

Thanks for the response Tyler, I feared this was the case. For posterity, I took Tyler's advice and wrote a custom normalizer that added a new textLink field (populated with the url string) to all custom post types that were experiencing this problem. The url string is conditionally assigned to textLink based on the node type. Here is my custom normalizer object, in full:

{
    // Normalizes the link format, so Gatsby doesn't drop
        // one type (link___NODE or external url string)
    name: `AcfLinkNormalizer`,
    normalizer: ({ entities }) => {
        const media = entities.filter(
            e => e.__type === `wordpress__wp_media`
        )
        return entities.map(e => {
            if (
                e.__type === 'wordpress__wp_surveys' ||
                e.__type === 'wordpress__wp_infographics' ||
                e.__type === 'wordpress__wp_annual_reports'
            ) {
                if (e.acf.link___NODE)
                    e.acf.textLink = media.find(
                        m => m.id === e.acf.link___NODE
                    ).source_url
                else e.acf.textLink = e.acf.link
            }
            return e
        })
    },
}
Was this page helpful?
0 / 5 - 0 ratings