Skip to content

Commit d3d5fd0

Browse files
gatsbybotTylerBarnespiehtyhopp
authored
fix(gatsby-source-wordpress): prevent inconsistent schema customization (#37749) (#37778)
* fix(gatsby-source-wordpress): prevent inconsistent schema customization (#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 (cherry picked from commit 9f26b67) * ignore browserlist update prompt * fix ignore * fix(gatsby-source-wordpress): Force removal of types (#37424) --------- Co-authored-by: Tyler Barnes <[email protected]> Co-authored-by: Michal Piechowiak <[email protected]> Co-authored-by: Ty Hopp <[email protected]>
1 parent 5bdef4a commit d3d5fd0

File tree

12 files changed

+312
-127
lines changed

12 files changed

+312
-127
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ jobs:
242242

243243
integration_tests_gatsby_source_wordpress:
244244
machine:
245-
image: "ubuntu-2004:202107-02"
245+
image: "ubuntu-2204:2023.02.1"
246246
steps:
247247
- run:
248248
command: |

integration-tests/gatsby-source-wordpress/__tests__/__snapshots__/index.js.snap

-30
Original file line numberDiff line numberDiff line change
@@ -999,12 +999,6 @@ Array [
999999
"fields": null,
10001000
"name": "WpAcfLinkFilterInput",
10011001
},
1002-
Object {
1003-
"fields": Array [
1004-
"node",
1005-
],
1006-
"name": "WpActionMonitorActionConnectionEdgeType",
1007-
},
10081002
Object {
10091003
"fields": Array [
10101004
"default",
@@ -5556,18 +5550,6 @@ Array [
55565550
],
55575551
"name": "WpEdgeType",
55585552
},
5559-
Object {
5560-
"fields": Array [
5561-
"node",
5562-
],
5563-
"name": "WpEnqueuedScriptConnectionEdgeType",
5564-
},
5565-
Object {
5566-
"fields": Array [
5567-
"node",
5568-
],
5569-
"name": "WpEnqueuedStylesheetConnectionEdgeType",
5570-
},
55715553
Object {
55725554
"fields": null,
55735555
"name": "WpFieldsEnum",
@@ -7237,12 +7219,6 @@ Array [
72377219
],
72387220
"name": "WpPage_Acfpagefields_repeaterField_RepeaterFlex_RepeaterFlexTitleLayout",
72397221
},
7240-
Object {
7241-
"fields": Array [
7242-
"node",
7243-
],
7244-
"name": "WpPluginConnectionEdgeType",
7245-
},
72467222
Object {
72477223
"fields": Array [
72487224
"author",
@@ -8457,12 +8433,6 @@ Array [
84578433
"fields": null,
84588434
"name": "WpTermNodeSortInput",
84598435
},
8460-
Object {
8461-
"fields": Array [
8462-
"node",
8463-
],
8464-
"name": "WpThemeConnectionEdgeType",
8465-
},
84668436
Object {
84678437
"fields": Array [
84688438
"author",

packages/gatsby-source-wordpress/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"gatsby-plugin-utils": "^3.19.0",
3333
"gatsby-source-filesystem": "^4.25.0",
3434
"glob": "^7.2.3",
35-
"got": "^11.8.5",
35+
"got": "^11.8.6",
36+
"json-diff": "^1.0.3",
3637
"lodash": "^4.17.21",
3738
"node-fetch": "^2.6.7",
3839
"p-queue": "^6.6.2",
@@ -60,8 +61,7 @@
6061
"identity-obj-proxy": "^3.0.0",
6162
"react-test-renderer": "^16.14.0",
6263
"rimraf": "^3.0.2",
63-
"tree-kill": "^1.2.2",
64-
"wait-on": "^4.0.2"
64+
"tree-kill": "^1.2.2"
6565
},
6666
"homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-wordpress#readme",
6767
"keywords": [

packages/gatsby-source-wordpress/src/models/gatsby-api.ts

-30
Original file line numberDiff line numberDiff line change
@@ -307,36 +307,6 @@ const defaultPluginOptions: IPluginOptions = {
307307
*/
308308
beforeChangeNode: menuBeforeChangeNode,
309309
},
310-
// the next two types can't be sourced in Gatsby properly yet
311-
// @todo instead of excluding these manually, auto exclude them
312-
// based on how they behave (no single node query available)
313-
EnqueuedScript: {
314-
exclude: true,
315-
},
316-
EnqueuedStylesheet: {
317-
exclude: true,
318-
},
319-
EnqueuedAsset: {
320-
exclude: true,
321-
},
322-
ContentNodeToEnqueuedScriptConnection: {
323-
exclude: true,
324-
},
325-
ContentNodeToEnqueuedStylesheetConnection: {
326-
exclude: true,
327-
},
328-
TermNodeToEnqueuedScriptConnection: {
329-
exclude: true,
330-
},
331-
TermNodeToEnqueuedStylesheetConnection: {
332-
exclude: true,
333-
},
334-
UserToEnqueuedScriptConnection: {
335-
exclude: true,
336-
},
337-
UserToEnqueuedStylesheetConnection: {
338-
exclude: true,
339-
},
340310
},
341311
}
342312

packages/gatsby-source-wordpress/src/steps/create-schema-customization/helpers.js

+151
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import store from "~/store"
22
import { typeDefinitionFilters } from "./type-filters"
33
import { getPluginOptions } from "~/utils/get-gatsby-api"
44
import { cloneDeep, merge } from "lodash"
5+
import { diffString } from "json-diff"
6+
import { formatLogMessage } from "../../utils/format-log-message"
7+
import { CODES } from "../../utils/report"
58

69
export const buildInterfacesListForType = type => {
710
let shouldAddNodeType = false
@@ -298,3 +301,151 @@ export const introspectionFieldTypeToSDL = fieldType => {
298301

299302
return openingTagsList.join(``) + closingTagsList.reverse().join(``)
300303
}
304+
305+
/**
306+
* This is an expensive fn but it doesn't matter because it's only to show a debugging warning message when something is wrong.
307+
*/
308+
function mergeDuplicateTypesAndReturnDedupedList(typeDefs) {
309+
const clonedDefs = cloneDeep(typeDefs)
310+
311+
const newList = []
312+
313+
for (const def of clonedDefs) {
314+
if (!def) {
315+
continue
316+
}
317+
318+
const duplicateDefs = clonedDefs.filter(
319+
d => d.config.name === def.config.name
320+
)
321+
322+
const newDef = {}
323+
324+
for (const dDef of duplicateDefs) {
325+
merge(newDef, dDef)
326+
}
327+
328+
newList.push(newDef)
329+
}
330+
331+
return newList
332+
}
333+
334+
/**
335+
* Diffs the built types between this build and the last one with the same remote schema hash.
336+
* This is to catch and add helpful error messages for when an inconsistent schema between builds is inadvertently created due to some bug
337+
*/
338+
export async function diffBuiltTypeDefs(typeDefs) {
339+
if (
340+
process.env.NODE_ENV !== `development` &&
341+
process.env.WP_DIFF_SCHEMA_CUSTOMIZATION !== `true`
342+
) {
343+
return
344+
}
345+
346+
const state = store.getState()
347+
348+
const {
349+
gatsbyApi: {
350+
helpers: { cache, reporter },
351+
},
352+
remoteSchema,
353+
} = state
354+
355+
const previousTypeDefinitions = await cache.get(`previousTypeDefinitions`)
356+
const typeDefString = JSON.stringify(typeDefs)
357+
const typeNames = typeDefs.map(typeDef => typeDef.config.name)
358+
359+
const remoteSchemaChanged =
360+
!previousTypeDefinitions ||
361+
previousTypeDefinitions?.schemaHash !== remoteSchema.schemaHash
362+
363+
if (remoteSchemaChanged) {
364+
await cache.set(`previousTypeDefinitions`, {
365+
schemaHash: remoteSchema.schemaHash,
366+
typeDefString,
367+
typeNames,
368+
})
369+
return
370+
}
371+
372+
// type defs are the same as last time, so don't check for missing/inconsistent types
373+
if (previousTypeDefinitions?.typeDefString === typeDefString) {
374+
return
375+
}
376+
377+
const missingTypeNames = previousTypeDefinitions.typeNames.filter(
378+
name => !typeNames.includes(name)
379+
)
380+
381+
const previousTypeDefJson = mergeDuplicateTypesAndReturnDedupedList(
382+
JSON.parse(previousTypeDefinitions.typeDefString)
383+
)
384+
385+
const newParsedTypeDefs = mergeDuplicateTypesAndReturnDedupedList(
386+
JSON.parse(typeDefString)
387+
)
388+
389+
const changedTypeDefs = newParsedTypeDefs
390+
.map(typeDef => {
391+
const previousTypeDef = previousTypeDefJson.find(
392+
previousTypeDef => previousTypeDef.config.name === typeDef.config.name
393+
)
394+
395+
const isDifferent = diffString(previousTypeDef, typeDef)
396+
397+
if (isDifferent) {
398+
return `Typename ${typeDef.config.name} diff:\n${diffString(
399+
previousTypeDef,
400+
typeDef,
401+
{
402+
// diff again to also show unchanged lines
403+
full: true,
404+
}
405+
)}`
406+
}
407+
408+
return null
409+
})
410+
.filter(Boolean)
411+
412+
let errorMessage = formatLogMessage(
413+
`The remote WPGraphQL schema hasn't changed but local generated type definitions have. This is a bug, please open an issue on Github${
414+
missingTypeNames.length || changedTypeDefs.length
415+
? ` and include the following text.`
416+
: ``
417+
}.${
418+
missingTypeNames.length
419+
? `\n\nMissing type names: ${missingTypeNames.join(`\n`)}\n`
420+
: ``
421+
}${
422+
changedTypeDefs.length
423+
? `\n\nChanged type defs:\n\n${changedTypeDefs.join(`\n`)}`
424+
: ``
425+
}`
426+
)
427+
428+
const maxErrorLength = 5000
429+
430+
if (errorMessage.length > maxErrorLength) {
431+
errorMessage =
432+
errorMessage.substring(0, maxErrorLength) +
433+
`\n\n...\n[Diff exceeded ${maxErrorLength} characters and was truncated]`
434+
}
435+
436+
if (process.env.WP_INCONSISTENT_SCHEMA_WARN !== `true`) {
437+
reporter.info(
438+
formatLogMessage(
439+
`Panicking due to inconsistent schema customization. Turn this into a warning by setting process.env.WP_INCONSISTENT_SCHEMA_WARN to a string of "true"`
440+
)
441+
)
442+
reporter.panic({
443+
id: CODES.InconsistentSchemaCustomization,
444+
context: {
445+
sourceMessage: errorMessage,
446+
},
447+
})
448+
} else {
449+
reporter.warn(errorMessage)
450+
}
451+
}

packages/gatsby-source-wordpress/src/steps/create-schema-customization/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import store from "~/store"
22

3-
import { buildInterfacesListForType, fieldOfTypeWasFetched } from "./helpers"
3+
import { diffBuiltTypeDefs, fieldOfTypeWasFetched } from "./helpers"
44

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

108+
diffBuiltTypeDefs(typeDefs)
108109
actions.createTypes(typeDefs)
109110
}
110111

packages/gatsby-source-wordpress/src/steps/ingest-remote-schema/build-queries-from-introspection/recursively-transform-fields.js

+14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
fieldIsExcludedOnAll,
1010
} from "~/steps/ingest-remote-schema/is-excluded"
1111
import { returnAliasedFieldName } from "~/steps/create-schema-customization/transform-fields"
12+
import { typeIsExcluded } from "../is-excluded"
1213

1314
export const transformInlineFragments = ({
1415
possibleTypes,
@@ -63,6 +64,15 @@ export const transformInlineFragments = ({
6364
return false
6465
}
6566

67+
if (
68+
typeIsExcluded({
69+
pluginOptions,
70+
typeName: findNamedTypeName(type),
71+
})
72+
) {
73+
return false
74+
}
75+
6676
possibleType.type = { ...type }
6777

6878
// save this type so we can use it in schema customization
@@ -531,6 +541,10 @@ const transformFields = ({
531541
!fieldIsExcludedOnAll({
532542
pluginOptions,
533543
field,
544+
}) &&
545+
!typeIsExcluded({
546+
pluginOptions,
547+
typeName: findNamedTypeName(field.type),
534548
})
535549
)
536550
.map(field => {

0 commit comments

Comments
 (0)