Skip to content

Commit

Permalink
fix(gatsby-source-wordpress): prevent inconsistent schema customizati…
Browse files Browse the repository at this point in the history
…on (#37749)

* panic or warn when schema customization is inconsistent

* ensure types are always properly excluded

* add blocklist of typename parts which cause a type to be excluded if it's name includes a part
  • Loading branch information
TylerBarnes committed Mar 16, 2023
1 parent 438ddda commit 9f26b67
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ jobs:

integration_tests_gatsby_source_wordpress:
machine:
image: "ubuntu-2204:2022.10.1"
image: "ubuntu-2204:2023.02.1"
steps:
- run:
command: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1015,12 +1015,6 @@ Array [
"fields": null,
"name": "WpAcfLinkSortInput",
},
Object {
"fields": Array [
"node",
],
"name": "WpActionMonitorActionConnectionEdgeType",
},
Object {
"fields": Array [
"default",
Expand Down Expand Up @@ -5716,18 +5710,6 @@ Array [
],
"name": "WpEdgeType",
},
Object {
"fields": Array [
"node",
],
"name": "WpEnqueuedScriptConnectionEdgeType",
},
Object {
"fields": Array [
"node",
],
"name": "WpEnqueuedStylesheetConnectionEdgeType",
},
Object {
"fields": null,
"name": "WpFieldSelector",
Expand Down Expand Up @@ -7549,12 +7531,6 @@ Array [
],
"name": "WpPage_Acfpagefields_repeaterField_RepeaterFlex_RepeaterFlexTitleLayout",
},
Object {
"fields": Array [
"node",
],
"name": "WpPluginConnectionEdgeType",
},
Object {
"fields": Array [
"author",
Expand Down Expand Up @@ -9113,12 +9089,6 @@ Array [
"fields": null,
"name": "WpTermNodeSortInput",
},
Object {
"fields": Array [
"node",
],
"name": "WpThemeConnectionEdgeType",
},
Object {
"fields": Array [
"author",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-source-wordpress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"gatsby-source-filesystem": "^5.8.0-next.0",
"glob": "^7.2.3",
"got": "^11.8.6",
"json-diff": "^1.0.3",
"lodash": "^4.17.21",
"node-fetch": "^2.6.8",
"p-queue": "^6.6.2",
Expand Down Expand Up @@ -60,8 +61,7 @@
"identity-obj-proxy": "^3.0.0",
"react-test-renderer": "^16.14.0",
"rimraf": "^3.0.2",
"tree-kill": "^1.2.2",
"wait-on": "^4.0.2"
"tree-kill": "^1.2.2"
},
"homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-wordpress#readme",
"keywords": [
Expand Down
30 changes: 0 additions & 30 deletions packages/gatsby-source-wordpress/src/models/gatsby-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,36 +307,6 @@ const defaultPluginOptions: IPluginOptions = {
*/
beforeChangeNode: menuBeforeChangeNode,
},
// the next two types can't be sourced in Gatsby properly yet
// @todo instead of excluding these manually, auto exclude them
// based on how they behave (no single node query available)
EnqueuedScript: {
exclude: true,
},
EnqueuedStylesheet: {
exclude: true,
},
EnqueuedAsset: {
exclude: true,
},
ContentNodeToEnqueuedScriptConnection: {
exclude: true,
},
ContentNodeToEnqueuedStylesheetConnection: {
exclude: true,
},
TermNodeToEnqueuedScriptConnection: {
exclude: true,
},
TermNodeToEnqueuedStylesheetConnection: {
exclude: true,
},
UserToEnqueuedScriptConnection: {
exclude: true,
},
UserToEnqueuedStylesheetConnection: {
exclude: true,
},
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import store from "~/store"
import { typeDefinitionFilters } from "./type-filters"
import { getPluginOptions } from "~/utils/get-gatsby-api"
import { cloneDeep, merge } from "lodash"
import { diffString } from "json-diff"
import { formatLogMessage } from "../../utils/format-log-message"
import { CODES } from "../../utils/report"

export const buildInterfacesListForType = type => {
let shouldAddNodeType = false
Expand Down Expand Up @@ -298,3 +301,151 @@ export const introspectionFieldTypeToSDL = fieldType => {

return openingTagsList.join(``) + closingTagsList.reverse().join(``)
}

/**
* This is an expensive fn but it doesn't matter because it's only to show a debugging warning message when something is wrong.
*/
function mergeDuplicateTypesAndReturnDedupedList(typeDefs) {
const clonedDefs = cloneDeep(typeDefs)

const newList = []

for (const def of clonedDefs) {
if (!def) {
continue
}

const duplicateDefs = clonedDefs.filter(
d => d.config.name === def.config.name
)

const newDef = {}

for (const dDef of duplicateDefs) {
merge(newDef, dDef)
}

newList.push(newDef)
}

return newList
}

/**
* Diffs the built types between this build and the last one with the same remote schema hash.
* This is to catch and add helpful error messages for when an inconsistent schema between builds is inadvertently created due to some bug
*/
export async function diffBuiltTypeDefs(typeDefs) {
if (
process.env.NODE_ENV !== `development` &&
process.env.WP_DIFF_SCHEMA_CUSTOMIZATION !== `true`
) {
return
}

const state = store.getState()

const {
gatsbyApi: {
helpers: { cache, reporter },
},
remoteSchema,
} = state

const previousTypeDefinitions = await cache.get(`previousTypeDefinitions`)
const typeDefString = JSON.stringify(typeDefs)
const typeNames = typeDefs.map(typeDef => typeDef.config.name)

const remoteSchemaChanged =
!previousTypeDefinitions ||
previousTypeDefinitions?.schemaHash !== remoteSchema.schemaHash

if (remoteSchemaChanged) {
await cache.set(`previousTypeDefinitions`, {
schemaHash: remoteSchema.schemaHash,
typeDefString,
typeNames,
})
return
}

// type defs are the same as last time, so don't check for missing/inconsistent types
if (previousTypeDefinitions?.typeDefString === typeDefString) {
return
}

const missingTypeNames = previousTypeDefinitions.typeNames.filter(
name => !typeNames.includes(name)
)

const previousTypeDefJson = mergeDuplicateTypesAndReturnDedupedList(
JSON.parse(previousTypeDefinitions.typeDefString)
)

const newParsedTypeDefs = mergeDuplicateTypesAndReturnDedupedList(
JSON.parse(typeDefString)
)

const changedTypeDefs = newParsedTypeDefs
.map(typeDef => {
const previousTypeDef = previousTypeDefJson.find(
previousTypeDef => previousTypeDef.config.name === typeDef.config.name
)

const isDifferent = diffString(previousTypeDef, typeDef)

if (isDifferent) {
return `Typename ${typeDef.config.name} diff:\n${diffString(
previousTypeDef,
typeDef,
{
// diff again to also show unchanged lines
full: true,
}
)}`
}

return null
})
.filter(Boolean)

let errorMessage = formatLogMessage(
`The remote WPGraphQL schema hasn't changed but local generated type definitions have. This is a bug, please open an issue on Github${
missingTypeNames.length || changedTypeDefs.length
? ` and include the following text.`
: ``
}.${
missingTypeNames.length
? `\n\nMissing type names: ${missingTypeNames.join(`\n`)}\n`
: ``
}${
changedTypeDefs.length
? `\n\nChanged type defs:\n\n${changedTypeDefs.join(`\n`)}`
: ``
}`
)

const maxErrorLength = 5000

if (errorMessage.length > maxErrorLength) {
errorMessage =
errorMessage.substring(0, maxErrorLength) +
`\n\n...\n[Diff exceeded ${maxErrorLength} characters and was truncated]`
}

if (process.env.WP_INCONSISTENT_SCHEMA_WARN !== `true`) {
reporter.info(
formatLogMessage(
`Panicking due to inconsistent schema customization. Turn this into a warning by setting process.env.WP_INCONSISTENT_SCHEMA_WARN to a string of "true"`
)
)
reporter.panic({
id: CODES.InconsistentSchemaCustomization,
context: {
sourceMessage: errorMessage,
},
})
} else {
reporter.warn(errorMessage)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import store from "~/store"

import { buildInterfacesListForType, fieldOfTypeWasFetched } from "./helpers"
import { diffBuiltTypeDefs, fieldOfTypeWasFetched } from "./helpers"

import buildType from "./build-types"
import { getGatsbyNodeTypeNames } from "../source-nodes/fetch-nodes/fetch-nodes"
Expand Down Expand Up @@ -105,6 +105,7 @@ const customizeSchema = async ({ actions, schema, store: gatsbyStore }) => {
)
)

diffBuiltTypeDefs(typeDefs)
actions.createTypes(typeDefs)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
fieldIsExcludedOnAll,
} from "~/steps/ingest-remote-schema/is-excluded"
import { returnAliasedFieldName } from "~/steps/create-schema-customization/transform-fields"
import { typeIsExcluded } from "../is-excluded"

export const transformInlineFragments = ({
possibleTypes,
Expand Down Expand Up @@ -63,6 +64,15 @@ export const transformInlineFragments = ({
return false
}

if (
typeIsExcluded({
pluginOptions,
typeName: findNamedTypeName(type),
})
) {
return false
}

possibleType.type = { ...type }

// save this type so we can use it in schema customization
Expand Down Expand Up @@ -531,6 +541,10 @@ const transformFields = ({
!fieldIsExcludedOnAll({
pluginOptions,
field,
}) &&
!typeIsExcluded({
pluginOptions,
typeName: findNamedTypeName(field.type),
})
)
.map(field => {
Expand Down

0 comments on commit 9f26b67

Please sign in to comment.