Skip to content

Commit

Permalink
feat(gatsby): Allow printing type definitions to file (schema lock-do…
Browse files Browse the repository at this point in the history
…wn) (#16291)

* Allow printing type definitions to file

* Fix docs

* Remove debug

* Cleanup

* Fix Loki dejavu
  • Loading branch information
stefanprobst authored and freiksenet committed Aug 9, 2019
1 parent 90a345e commit 23a460a
Show file tree
Hide file tree
Showing 9 changed files with 849 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/gatsby/package.json
Expand Up @@ -75,7 +75,7 @@
"gatsby-telemetry": "^1.1.11",
"glob": "^7.1.1",
"got": "8.0.0",
"graphql": "^14.1.1",
"graphql": "^14.2.0",
"graphql-compose": "^6.3.2",
"graphql-playground-middleware-express": "^1.7.10",
"invariant": "^2.2.4",
Expand Down
57 changes: 55 additions & 2 deletions packages/gatsby/src/redux/actions/restricted.js
Expand Up @@ -80,7 +80,7 @@ import type GatsbyGraphQLType from "../../schema/types/type-builders"
*
*
* @example
* exports.sourceNodes = ({ actions }) => {
* exports.createSchemaCustomization = ({ actions }) => {
* const { createTypes } = actions
* const typeDefs = `
* """
Expand Down Expand Up @@ -114,7 +114,7 @@ import type GatsbyGraphQLType from "../../schema/types/type-builders"
* }
*
* // using Gatsby Type Builder API
* exports.sourceNodes = ({ actions, schema }) => {
* exports.createSchemaCustomization = ({ actions, schema }) => {
* const { createTypes } = actions
* const typeDefs = [
* schema.buildObjectType({
Expand Down Expand Up @@ -262,6 +262,56 @@ actions.createFieldExtension = (
}
}

/**
* Write GraphQL schema to file
*
* Writes out inferred and explicitly specified type definitions. This is not
* the full GraphQL schema, but only the types necessary to recreate all type
* definitions, i.e. it does not include directives, built-ins, and derived
* types for filtering, sorting, pagination etc. Optionally, you can define a
* list of types to include/exclude. This is recommended to avoid including
* definitions for plugin-created types.
*
* @availableIn [createSchemaCustomization]
*
* @param {object} $0
* @param {string} [$0.path] The path to the output file, defaults to `schema.gql`
* @param {object} [$0.include] Configure types to include
* @param {string[]} [$0.include.types] Only include these types
* @param {string[]} [$0.include.plugins] Only include types owned by these plugins
* @param {object} [$0.exclude] Configure types to exclude
* @param {string[]} [$0.exclude.types] Do not include these types
* @param {string[]} [$0.exclude.plugins] Do not include types owned by these plugins
* @param {boolean} [withFieldTypes] Include field types, defaults to `true`
*/
actions.printTypeDefinitions = (
{
path = `schema.gql`,
include,
exclude,
withFieldTypes = true,
}: {
path?: string,
include?: { types?: Array<string>, plugins?: Array<string> },
exclude?: { types?: Array<string>, plugins?: Array<string> },
withFieldTypes?: boolean,
},
plugin: Plugin,
traceId?: string
) => {
return {
type: `PRINT_SCHEMA_REQUESTED`,
plugin,
traceId,
payload: {
path,
include,
exclude,
withFieldTypes,
},
}
}

/**
* Make functionality available on field resolver `context`
*
Expand Down Expand Up @@ -398,6 +448,9 @@ const availableActionsByAPI = mapAvailableActionsToAPIs({
[ALLOWED_IN]: [`sourceNodes`, `createSchemaCustomization`],
[DEPRECATED_IN]: [`onPreInit`, `onPreBootstrap`],
},
printTypeDefinitions: {
[ALLOWED_IN]: [`createSchemaCustomization`],
},
})

module.exports = { actions, availableActionsByAPI }
14 changes: 14 additions & 0 deletions packages/gatsby/src/redux/reducers/schema-customization.js
Expand Up @@ -3,6 +3,7 @@ module.exports = (
composer: null,
context: {},
fieldExtensions: {},
printConfig: null,
thirdPartySchemas: [],
types: [],
},
Expand Down Expand Up @@ -49,6 +50,18 @@ module.exports = (
fieldExtensions: { ...state.fieldExtensions, [name]: extension },
}
}
case `PRINT_SCHEMA_REQUESTED`: {
const { path, include, exclude, withFieldTypes } = action.payload
return {
...state,
printConfig: {
path,
include,
exclude,
withFieldTypes,
},
}
}
case `CREATE_RESOLVER_CONTEXT`: {
const context = action.payload
return {
Expand All @@ -61,6 +74,7 @@ module.exports = (
composer: null,
context: {},
fieldExtensions: {},
printConfig: null,
thirdPartySchemas: [],
types: [],
}
Expand Down
171 changes: 171 additions & 0 deletions packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap
@@ -0,0 +1,171 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Print type definitions allows explicitly listing types to include without including field types 1`] = `
"### Type definitions saved at 2019-01-01 ###
type InlineTest implements Node & ITest @childOf(types: [\\"OneMoreTest\\"]) @dontInfer {
first: Inline
second(bar: Baz): Date @dateformat(formatString: \\"MM/DD/YYYY\\")
}"
`;

exports[`Print type definitions allows specifying types owned by plugins to exclude 1`] = `
"### Type definitions saved at 2019-01-01 ###
type InlineTest implements Node & ITest @childOf(types: [\\"OneMoreTest\\"]) @dontInfer {
first: Inline
second(bar: Baz): Date @dateformat(formatString: \\"MM/DD/YYYY\\")
}
interface ITest @nodeInterface {
id: ID!
date: Date @dateformat(formatString: \\"YYYY\\")
}
type Inline {
foo: Nested
}
type Nested {
baz: Boolean
}
input Baz {
qux: Boolean
}
type Test implements Node @dontInfer {
foo: Int
}"
`;

exports[`Print type definitions allows specifying types owned by plugins to include 1`] = `
"### Type definitions saved at 2019-01-01 ###
type InlineTest implements Node & ITest @childOf(types: [\\"OneMoreTest\\"]) @dontInfer {
first: Inline
second(bar: Baz): Date @dateformat(formatString: \\"MM/DD/YYYY\\")
}
interface ITest @nodeInterface {
id: ID!
date: Date @dateformat(formatString: \\"YYYY\\")
}
type Inline {
foo: Nested
}
type Nested {
baz: Boolean
}
input Baz {
qux: Boolean
}"
`;

exports[`Print type definitions allows specifying types to exclude 1`] = `
"### Type definitions saved at 2019-01-01 ###
type AnotherTest implements Node & ITest @dontInfer {
nested: Nested
date: Date @dateformat(formatString: \\"YYYY\\")
hello(planet: String = \\"world\\", language: [Language!]!): String!
}
interface ITest @nodeInterface {
id: ID!
date: Date @dateformat(formatString: \\"YYYY\\")
}
type Nested {
baz: Boolean
}
input Language {
lang: String
hello: String
}
type OneMoreTest implements Node @dontInfer {
bar: Boolean
}
union ThisOrThat = AnotherTest | OneMoreTest
type Test implements Node @dontInfer {
foo: Int
}"
`;

exports[`Print type definitions allows specifying types to include (and includes interfaces and field types) 1`] = `
"### Type definitions saved at 2019-01-01 ###
type AnotherTest implements Node & ITest @dontInfer {
nested: Nested
date: Date @dateformat(formatString: \\"YYYY\\")
hello(planet: String = \\"world\\", language: [Language!]!): String!
}
interface ITest @nodeInterface {
id: ID!
date: Date @dateformat(formatString: \\"YYYY\\")
}
type Nested {
baz: Boolean
}
input Language {
lang: String
hello: String
}"
`;

exports[`Print type definitions saves correct type definitions 1`] = `
"### Type definitions saved at 2019-01-01 ###
type AnotherTest implements Node & ITest @dontInfer {
nested: Nested
date: Date @dateformat(formatString: \\"YYYY\\")
hello(planet: String = \\"world\\", language: [Language!]!): String!
}
interface ITest @nodeInterface {
id: ID!
date: Date @dateformat(formatString: \\"YYYY\\")
}
type Nested {
baz: Boolean
}
input Language {
lang: String
hello: String
}
type OneMoreTest implements Node @dontInfer {
bar: Boolean
}
union ThisOrThat = AnotherTest | OneMoreTest
type InlineTest implements Node & ITest @childOf(types: [\\"OneMoreTest\\"]) @dontInfer {
first: Inline
second(bar: Baz): Date @dateformat(formatString: \\"MM/DD/YYYY\\")
}
type Inline {
foo: Nested
}
input Baz {
qux: Boolean
}
type Test implements Node @dontInfer {
foo: Int
}"
`;

0 comments on commit 23a460a

Please sign in to comment.