Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(gatsby-source-wordpress): prevent inconsistent schema customization #37749

Merged
merged 17 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading