Files
30-seconds-of-code/node_modules/gatsby/dist/schema/schema.js
2019-08-20 15:52:05 +02:00

1097 lines
31 KiB
JavaScript

"use strict";
const _ = require(`lodash`);
const invariant = require(`invariant`);
const {
isSpecifiedScalarType,
isIntrospectionType,
assertValidName,
parse,
GraphQLNonNull,
GraphQLList
} = require(`graphql`);
const {
ObjectTypeComposer,
InterfaceTypeComposer,
UnionTypeComposer,
InputTypeComposer,
ScalarTypeComposer,
EnumTypeComposer
} = require(`graphql-compose`);
const apiRunner = require(`../utils/api-runner-node`);
const report = require(`gatsby-cli/lib/reporter`);
const {
addNodeInterfaceFields
} = require(`./types/node-interface`);
const {
addInferredType,
addInferredTypes
} = require(`./infer`);
const {
findOne,
findManyPaginated
} = require(`./resolvers`);
const {
processFieldExtensions,
internalExtensionNames
} = require(`./extensions`);
const {
getPagination
} = require(`./types/pagination`);
const {
getSortInput
} = require(`./types/sort`);
const {
getFilterInput
} = require(`./types/filter`);
const {
isGatsbyType,
GatsbyGraphQLTypeKind
} = require(`./types/type-builders`);
const {
printTypeDefinitions
} = require(`./print`);
const buildSchema = async ({
schemaComposer,
nodeStore,
types,
typeMapping,
fieldExtensions,
thirdPartySchemas,
printConfig,
typeConflictReporter,
parentSpan
}) => {
await updateSchemaComposer({
schemaComposer,
nodeStore,
types,
typeMapping,
fieldExtensions,
thirdPartySchemas,
printConfig,
typeConflictReporter,
parentSpan
}); // const { printSchema } = require(`graphql`)
const schema = schemaComposer.buildSchema(); // console.log(printSchema(schema))
return schema;
};
const rebuildSchemaWithSitePage = async ({
schemaComposer,
nodeStore,
typeMapping,
fieldExtensions,
typeConflictReporter,
parentSpan
}) => {
const typeComposer = schemaComposer.getOTC(`SitePage`);
const shouldInfer = !typeComposer.hasExtension(`infer`) || typeComposer.getExtension(`infer`) !== false;
if (shouldInfer) {
addInferredType({
schemaComposer,
typeComposer,
nodeStore,
typeConflictReporter,
typeMapping,
parentSpan
});
}
await processTypeComposer({
schemaComposer,
typeComposer,
fieldExtensions,
nodeStore,
parentSpan
});
return schemaComposer.buildSchema();
};
module.exports = {
buildSchema,
rebuildSchemaWithSitePage
};
const updateSchemaComposer = async ({
schemaComposer,
nodeStore,
types,
typeMapping,
fieldExtensions,
thirdPartySchemas,
printConfig,
typeConflictReporter,
parentSpan
}) => {
await addTypes({
schemaComposer,
parentSpan,
types
});
await addInferredTypes({
schemaComposer,
nodeStore,
typeConflictReporter,
typeMapping,
parentSpan
});
await printTypeDefinitions({
config: printConfig,
schemaComposer
});
await addSetFieldsOnGraphQLNodeTypeFields({
schemaComposer,
nodeStore,
parentSpan
});
await Promise.all(Array.from(new Set(schemaComposer.values())).map(typeComposer => processTypeComposer({
schemaComposer,
typeComposer,
fieldExtensions,
nodeStore,
parentSpan
})));
checkQueryableInterfaces({
schemaComposer
});
await addConvenienceChildrenFields({
schemaComposer,
parentSpan
});
await addThirdPartySchemas({
schemaComposer,
thirdPartySchemas,
parentSpan
});
await addCustomResolveFunctions({
schemaComposer,
parentSpan
});
};
const processTypeComposer = async ({
schemaComposer,
typeComposer,
fieldExtensions,
nodeStore,
parentSpan
}) => {
if (typeComposer instanceof ObjectTypeComposer) {
await processFieldExtensions({
schemaComposer,
typeComposer,
fieldExtensions,
parentSpan
});
if (typeComposer.hasInterface(`Node`)) {
await addNodeInterfaceFields({
schemaComposer,
typeComposer,
parentSpan
});
await addImplicitConvenienceChildrenFields({
schemaComposer,
typeComposer,
nodeStore,
parentSpan
});
await addTypeToRootQuery({
schemaComposer,
typeComposer,
parentSpan
});
}
} else if (typeComposer instanceof InterfaceTypeComposer) {
if (typeComposer.getExtension(`nodeInterface`)) {
// We only process field extensions for queryable Node interfaces, so we get
// the input args on the root query type, e.g. `formatString` etc. for `dateformat`
await processFieldExtensions({
schemaComposer,
typeComposer,
fieldExtensions,
parentSpan
});
await addTypeToRootQuery({
schemaComposer,
typeComposer,
parentSpan
});
}
}
};
const addTypes = ({
schemaComposer,
types,
parentSpan
}) => {
types.forEach(({
typeOrTypeDef,
plugin
}) => {
if (typeof typeOrTypeDef === `string`) {
let parsedTypes;
const createdFrom = `sdl`;
try {
parsedTypes = parseTypeDefs({
typeDefs: typeOrTypeDef,
plugin,
createdFrom,
schemaComposer,
parentSpan
});
} catch (error) {
reportParsingError(error);
return;
}
parsedTypes.forEach(type => {
processAddedType({
schemaComposer,
type,
parentSpan,
createdFrom,
plugin
});
});
} else if (isGatsbyType(typeOrTypeDef)) {
const type = createTypeComposerFromGatsbyType({
schemaComposer,
type: typeOrTypeDef,
parentSpan
});
if (type) {
const typeName = type.getTypeName();
const createdFrom = `typeBuilder`;
checkIsAllowedTypeName(typeName);
if (schemaComposer.has(typeName)) {
const typeComposer = schemaComposer.get(typeName);
mergeTypes({
schemaComposer,
typeComposer,
type,
plugin,
createdFrom,
parentSpan
});
} else {
processAddedType({
schemaComposer,
type,
parentSpan,
createdFrom,
plugin
});
}
}
} else {
const typeName = typeOrTypeDef.name;
const createdFrom = `graphql-js`;
checkIsAllowedTypeName(typeName);
if (schemaComposer.has(typeName)) {
const typeComposer = schemaComposer.get(typeName);
mergeTypes({
schemaComposer,
typeComposer,
type: typeOrTypeDef,
plugin,
createdFrom,
parentSpan
});
} else {
processAddedType({
schemaComposer,
type: typeOrTypeDef,
parentSpan,
createdFrom,
plugin
});
}
}
});
};
const mergeTypes = ({
schemaComposer,
typeComposer,
type,
plugin,
createdFrom,
parentSpan
}) => {
// Only allow user or plugin owning the type to extend already existing type.
const typeOwner = typeComposer.getExtension(`plugin`);
if (!plugin || plugin.name === `default-site-plugin` || plugin.name === typeOwner) {
typeComposer.merge(type);
if (isNamedTypeComposer(type)) {
typeComposer.extendExtensions(type.getExtensions());
}
addExtensions({
schemaComposer,
typeComposer,
plugin,
createdFrom
});
return true;
} else {
report.warn(`Plugin \`${plugin.name}\` tried to define the GraphQL type ` + `\`${typeComposer.getTypeName()}\`, which has already been defined ` + `by the plugin \`${typeOwner}\`.`);
return false;
}
};
const processAddedType = ({
schemaComposer,
type,
parentSpan,
createdFrom,
plugin
}) => {
const typeName = schemaComposer.addAsComposer(type);
const typeComposer = schemaComposer.get(typeName);
if (typeComposer instanceof InterfaceTypeComposer || typeComposer instanceof UnionTypeComposer) {
if (!typeComposer.getResolveType()) {
typeComposer.setResolveType(node => node.internal.type);
}
}
schemaComposer.addSchemaMustHaveType(typeComposer);
addExtensions({
schemaComposer,
typeComposer,
plugin,
createdFrom
});
return typeComposer;
};
const addExtensions = ({
schemaComposer,
typeComposer,
plugin,
createdFrom
}) => {
typeComposer.setExtension(`createdFrom`, createdFrom);
typeComposer.setExtension(`plugin`, plugin ? plugin.name : null);
if (createdFrom === `sdl`) {
const directives = typeComposer.getDirectives();
directives.forEach(({
name,
args
}) => {
switch (name) {
case `infer`:
case `dontInfer`:
typeComposer.setExtension(`infer`, name === `infer`);
if (args.noDefaultResolvers != null) {
typeComposer.setExtension(`addDefaultResolvers`, !args.noDefaultResolvers);
}
break;
case `mimeTypes`:
typeComposer.setExtension(`mimeTypes`, args);
break;
case `childOf`:
typeComposer.setExtension(`childOf`, args);
break;
case `nodeInterface`:
if (typeComposer instanceof InterfaceTypeComposer) {
if (!typeComposer.hasField(`id`) || typeComposer.getFieldType(`id`).toString() !== `ID!`) {
report.panic(`Interfaces with the \`nodeInterface\` extension must have a field ` + `\`id\` of type \`ID!\`. Check the type definition of ` + `\`${typeComposer.getTypeName()}\`.`);
}
typeComposer.setExtension(`nodeInterface`, true);
}
break;
default:
}
});
}
if (typeComposer instanceof ObjectTypeComposer || typeComposer instanceof InterfaceTypeComposer || typeComposer instanceof InputTypeComposer) {
typeComposer.getFieldNames().forEach(fieldName => {
typeComposer.setFieldExtension(fieldName, `createdFrom`, createdFrom);
typeComposer.setFieldExtension(fieldName, `plugin`, plugin ? plugin.name : null);
if (createdFrom === `sdl`) {
const directives = typeComposer.getFieldDirectives(fieldName);
directives.forEach(({
name,
args
}) => {
typeComposer.setFieldExtension(fieldName, name, args);
});
} // Validate field extension args. `graphql-compose` already checks the
// type of directive args in `parseDirectives`, but we want to check
// extensions provided with type builders as well. Also, we warn if an
// extension option was provided which does not exist in the field
// extension definition.
const fieldExtensions = typeComposer.getFieldExtensions(fieldName);
const typeName = typeComposer.getTypeName();
Object.keys(fieldExtensions).filter(name => !internalExtensionNames.includes(name)).forEach(name => {
const args = fieldExtensions[name];
if (!args || typeof args !== `object`) {
report.error(`Field extension arguments must be provided as an object. ` + `Received "${args}" on \`${typeName}.${fieldName}\`.`);
return;
}
try {
const definition = schemaComposer.getDirective(name); // Handle `defaultValue` when not provided as directive
definition.args.forEach(({
name,
defaultValue
}) => {
if (args[name] === undefined && defaultValue !== undefined) {
args[name] = defaultValue;
}
});
Object.keys(args).forEach(arg => {
const argumentDef = definition.args.find(({
name
}) => name === arg);
if (!argumentDef) {
report.error(`Field extension \`${name}\` on \`${typeName}.${fieldName}\` ` + `has invalid argument \`${arg}\`.`);
return;
}
const value = args[arg];
try {
validate(argumentDef.type, value);
} catch (error) {
report.error(`Field extension \`${name}\` on \`${typeName}.${fieldName}\` ` + `has argument \`${arg}\` with invalid value "${value}". ` + error.message);
}
});
} catch (error) {
report.error(`Field extension \`${name}\` on \`${typeName}.${fieldName}\` ` + `is not available.`);
}
});
});
}
if (typeComposer.hasExtension(`addDefaultResolvers`)) {
report.warn(`Deprecation warning - "noDefaultResolvers" is deprecated. In Gatsby 3, ` + `defined fields won't get resolvers, unless explicitly added with a ` + `directive/extension.`);
}
return typeComposer;
};
const checkIsAllowedTypeName = name => {
invariant(name !== `Node`, `The GraphQL type \`Node\` is reserved for internal use.`);
invariant(!name.endsWith(`FilterInput`) && !name.endsWith(`SortInput`), `GraphQL type names ending with "FilterInput" or "SortInput" are ` + `reserved for internal use. Please rename \`${name}\`.`);
invariant(![`Boolean`, `Date`, `Float`, `ID`, `Int`, `JSON`, `String`].includes(name), `The GraphQL type \`${name}\` is reserved for internal use by ` + `built-in scalar types.`);
assertValidName(name);
};
const createTypeComposerFromGatsbyType = ({
schemaComposer,
type,
parentSpan
}) => {
switch (type.kind) {
case GatsbyGraphQLTypeKind.OBJECT:
{
return ObjectTypeComposer.createTemp(Object.assign({}, type.config, {
interfaces: () => {
if (type.config.interfaces) {
return type.config.interfaces.map(iface => {
if (typeof iface === `string`) {
return schemaComposer.getIFTC(iface).getType();
} else {
return iface;
}
});
} else {
return [];
}
}
}), schemaComposer);
}
case GatsbyGraphQLTypeKind.INPUT_OBJECT:
{
return InputTypeComposer.createTemp(type.config, schemaComposer);
}
case GatsbyGraphQLTypeKind.UNION:
{
return UnionTypeComposer.createTemp(Object.assign({}, type.config, {
types: () => {
if (type.config.types) {
return type.config.types.map(typeName => schemaComposer.getOTC(typeName).getType());
} else {
return [];
}
}
}), schemaComposer);
}
case GatsbyGraphQLTypeKind.INTERFACE:
{
return InterfaceTypeComposer.createTemp(type.config, schemaComposer);
}
case GatsbyGraphQLTypeKind.ENUM:
{
return EnumTypeComposer.createTemp(type.config, schemaComposer);
}
case GatsbyGraphQLTypeKind.SCALAR:
{
return ScalarTypeComposer.createTemp(type.config, schemaComposer);
}
default:
{
report.warn(`Illegal type definition: ${JSON.stringify(type.config)}`);
return null;
}
}
};
const addSetFieldsOnGraphQLNodeTypeFields = ({
schemaComposer,
nodeStore,
parentSpan
}) => Promise.all(Array.from(schemaComposer.values()).map(async tc => {
if (tc instanceof ObjectTypeComposer && tc.hasInterface(`Node`)) {
const typeName = tc.getTypeName();
const result = await apiRunner(`setFieldsOnGraphQLNodeType`, {
type: {
name: typeName,
nodes: nodeStore.getNodesByType(typeName)
},
traceId: `initial-setFieldsOnGraphQLNodeType`,
parentSpan
});
if (result) {
// NOTE: `setFieldsOnGraphQLNodeType` only allows setting
// nested fields with a path as property name, i.e.
// `{ 'frontmatter.published': 'Boolean' }`, but not in the form
// `{ frontmatter: { published: 'Boolean' }}`
result.forEach(fields => tc.addNestedFields(fields));
}
}
}));
const addThirdPartySchemas = ({
schemaComposer,
thirdPartySchemas,
parentSpan
}) => {
thirdPartySchemas.forEach(schema => {
const schemaQueryType = schema.getQueryType();
const queryTC = schemaComposer.createTempTC(schemaQueryType);
processThirdPartyTypeFields({
typeComposer: queryTC,
schemaQueryType
});
schemaComposer.Query.addFields(queryTC.getFields()); // Explicitly add the third-party schema's types, so they can be targeted
// in `createResolvers` API.
const types = schema.getTypeMap();
Object.keys(types).forEach(typeName => {
const type = types[typeName];
if (type !== schemaQueryType && !isSpecifiedScalarType(type) && !isIntrospectionType(type) && type.name !== `Date` && type.name !== `JSON`) {
const typeComposer = schemaComposer.createTC(type);
if (typeComposer instanceof ObjectTypeComposer || typeComposer instanceof InterfaceTypeComposer) {
processThirdPartyTypeFields({
typeComposer,
schemaQueryType
});
}
typeComposer.setExtension(`createdFrom`, `thirdPartySchema`);
schemaComposer.addSchemaMustHaveType(typeComposer);
}
});
});
};
const processThirdPartyTypeFields = ({
typeComposer,
schemaQueryType
}) => {
// Fix for types that refer to Query. Thanks Relay Classic!
typeComposer.getFieldNames().forEach(fieldName => {
const field = typeComposer.getField(fieldName);
const fieldType = field.type.toString();
if (fieldType.replace(/[[\]!]/g, ``) === schemaQueryType.name) {
typeComposer.extendField(fieldName, {
type: fieldType.replace(schemaQueryType.name, `Query`)
});
}
});
};
const addCustomResolveFunctions = async ({
schemaComposer,
parentSpan
}) => {
const intermediateSchema = schemaComposer.buildSchema();
const createResolvers = resolvers => {
Object.keys(resolvers).forEach(typeName => {
const fields = resolvers[typeName];
if (schemaComposer.has(typeName)) {
const tc = schemaComposer.getOTC(typeName);
Object.keys(fields).forEach(fieldName => {
const fieldConfig = fields[fieldName];
if (tc.hasField(fieldName)) {
const originalFieldConfig = tc.getFieldConfig(fieldName);
const originalTypeName = originalFieldConfig.type.toString();
const originalResolver = originalFieldConfig.resolve;
let fieldTypeName;
if (fieldConfig.type) {
fieldTypeName = Array.isArray(fieldConfig.type) ? stringifyArray(fieldConfig.type) : fieldConfig.type.toString();
}
if (!fieldTypeName || fieldTypeName.replace(/!/g, ``) === originalTypeName.replace(/!/g, ``) || tc.getExtension(`createdFrom`) === `thirdPartySchema`) {
const newConfig = {};
if (fieldConfig.type) {
newConfig.type = fieldConfig.type;
}
if (fieldConfig.args) {
newConfig.args = fieldConfig.args;
}
if (fieldConfig.resolve) {
newConfig.resolve = (source, args, context, info) => fieldConfig.resolve(source, args, context, Object.assign({}, info, {
originalResolver: originalResolver || context.defaultFieldResolver
}));
}
tc.extendField(fieldName, newConfig);
} else if (fieldTypeName) {
report.warn(`\`createResolvers\` passed resolvers for field ` + `\`${typeName}.${fieldName}\` with type \`${fieldTypeName}\`. ` + `Such a field with type \`${originalTypeName}\` already exists ` + `on the type. Use \`createTypes\` to override type fields.`);
}
} else {
tc.addFields({
[fieldName]: fieldConfig
});
}
});
} else {
report.warn(`\`createResolvers\` passed resolvers for type \`${typeName}\` that ` + `doesn't exist in the schema. Use \`createTypes\` to add the type ` + `before adding resolvers.`);
}
});
};
await apiRunner(`createResolvers`, {
intermediateSchema,
createResolvers,
traceId: `initial-createResolvers`,
parentSpan
});
};
const addConvenienceChildrenFields = ({
schemaComposer
}) => {
const parentTypesToChildren = new Map();
const mimeTypesToChildren = new Map();
const typesHandlingMimeTypes = new Map();
schemaComposer.forEach(type => {
if ((type instanceof ObjectTypeComposer || type instanceof InterfaceTypeComposer) && type.hasExtension(`mimeTypes`)) {
const {
types
} = type.getExtension(`mimeTypes`);
new Set(types).forEach(mimeType => {
if (!typesHandlingMimeTypes.has(mimeType)) {
typesHandlingMimeTypes.set(mimeType, new Set());
}
typesHandlingMimeTypes.get(mimeType).add(type);
});
}
if ((type instanceof ObjectTypeComposer || type instanceof InterfaceTypeComposer) && type.hasExtension(`childOf`)) {
if (type instanceof ObjectTypeComposer && !type.hasInterface(`Node`)) {
report.error(`The \`childOf\` extension can only be used on types that implement the \`Node\` interface.\n` + `Check the type definition of \`${type.getTypeName()}\`.`);
return;
}
if (type instanceof InterfaceTypeComposer && !type.hasExtension(`nodeInterface`)) {
report.error(`The \`childOf\` extension can only be used on interface types that ` + `have the \`@nodeInterface\` extension.\n` + `Check the type definition of \`${type.getTypeName()}\`.`);
return;
}
const {
types,
mimeTypes,
many
} = type.getExtension(`childOf`);
new Set(types).forEach(parentType => {
if (!parentTypesToChildren.has(parentType)) {
parentTypesToChildren.set(parentType, new Map());
}
parentTypesToChildren.get(parentType).set(type, many);
});
new Set(mimeTypes).forEach(mimeType => {
if (!mimeTypesToChildren.has(mimeType)) {
mimeTypesToChildren.set(mimeType, new Map());
}
mimeTypesToChildren.get(mimeType).set(type, many);
});
}
});
parentTypesToChildren.forEach((children, parent) => {
if (!schemaComposer.has(parent)) return;
const typeComposer = schemaComposer.getAnyTC(parent);
if (typeComposer instanceof InterfaceTypeComposer && !typeComposer.hasExtension(`nodeInterface`)) {
report.error(`With the \`childOf\` extension, children fields can only be added to ` + `interfaces which have the \`@nodeInterface\` extension.\n` + `Check the type definition of \`${typeComposer.getTypeName()}\`.`);
return;
}
children.forEach((many, child) => {
if (many) {
typeComposer.addFields(createChildrenField(child.getTypeName()));
} else {
typeComposer.addFields(createChildField(child.getTypeName()));
}
});
});
mimeTypesToChildren.forEach((children, mimeType) => {
const parentTypes = typesHandlingMimeTypes.get(mimeType);
if (parentTypes) {
parentTypes.forEach(typeComposer => {
if (typeComposer instanceof InterfaceTypeComposer && !typeComposer.hasExtension(`nodeInterface`)) {
report.error(`With the \`childOf\` extension, children fields can only be added to ` + `interfaces which have the \`@nodeInterface\` extension.\n` + `Check the type definition of \`${typeComposer.getTypeName()}\`.`);
return;
}
children.forEach((many, child) => {
if (many) {
typeComposer.addFields(createChildrenField(child.getTypeName()));
} else {
typeComposer.addFields(createChildField(child.getTypeName()));
}
});
});
}
});
};
const addImplicitConvenienceChildrenFields = ({
schemaComposer,
typeComposer,
nodeStore
}) => {
const shouldInfer = typeComposer.getExtension(`infer`); // In Gatsby v3, when `@dontInfer` is set, children fields will not be
// created for parent-child relations set by plugins with
// `createParentChildLink`. With `@dontInfer`, only parent-child
// relations explicitly set with the `childOf` extension will be added.
// if (shouldInfer === false) return
const parentTypeName = typeComposer.getTypeName();
const nodes = nodeStore.getNodesByType(parentTypeName);
const childNodesByType = groupChildNodesByType({
nodeStore,
nodes
});
Object.keys(childNodesByType).forEach(typeName => {
const typeChildren = childNodesByType[typeName];
const maxChildCount = _.maxBy(_.values(_.groupBy(typeChildren, c => c.parent)), g => g.length).length; // Adding children fields to types with the `@dontInfer` extension is deprecated
if (shouldInfer === false) {
const childTypeComposer = schemaComposer.getAnyTC(typeName);
const childOfExtension = childTypeComposer.getExtension(`childOf`);
const many = maxChildCount > 1; // Only warn when the parent-child relation has not been explicitly set with
if (!childOfExtension || !childOfExtension.types.includes(parentTypeName) || !childOfExtension.many === many) {
const fieldName = _.camelCase(`${many ? `children` : `child`} ${typeName}`);
report.warn(`On types with the \`@dontInfer\` directive, or with the \`infer\` ` + `extension set to \`false\`, automatically adding fields for ` + `children types is deprecated.\n` + `In Gatsby v3, only children fields explicitly set with the ` + `\`childOf\` extension will be added.\n` + `For example, in Gatsby v3, \`${parentTypeName}\` will ` + `not get a \`${fieldName}\` field.`);
}
}
if (maxChildCount > 1) {
typeComposer.addFields(createChildrenField(typeName));
} else {
typeComposer.addFields(createChildField(typeName));
}
});
};
const createChildrenField = typeName => {
return {
[_.camelCase(`children ${typeName}`)]: {
type: () => [typeName],
resolve(source, args, context) {
const {
path
} = context;
return context.nodeModel.getNodesByIds({
ids: source.children,
type: typeName
}, {
path
});
}
}
};
};
const createChildField = typeName => {
return {
[_.camelCase(`child ${typeName}`)]: {
type: () => typeName,
async resolve(source, args, context) {
const {
path
} = context;
const result = await context.nodeModel.getNodesByIds({
ids: source.children,
type: typeName
}, {
path
});
if (result && result.length > 0) {
return result[0];
} else {
return null;
}
}
}
};
};
const groupChildNodesByType = ({
nodeStore,
nodes
}) => _(nodes).flatMap(node => (node.children || []).map(nodeStore.getNode)).groupBy(node => node.internal ? node.internal.type : undefined).value();
const addTypeToRootQuery = ({
schemaComposer,
typeComposer
}) => {
// TODO: We should have an abstraction for keeping and clearing
// related TypeComposers and InputTypeComposers.
// Also see the comment on the skipped test in `rebuild-schema`.
typeComposer.removeInputTypeComposer();
const sortInputTC = getSortInput({
schemaComposer,
typeComposer
});
const filterInputTC = getFilterInput({
schemaComposer,
typeComposer
});
const paginationTC = getPagination({
schemaComposer,
typeComposer
});
const typeName = typeComposer.getTypeName(); // not strictly correctly, result is `npmPackage` and `allNpmPackage` from type `NPMPackage`
const queryName = _.camelCase(typeName);
const queryNamePlural = _.camelCase(`all ${typeName}`);
schemaComposer.Query.addFields({
[queryName]: {
type: typeComposer,
args: Object.assign({}, filterInputTC.getFields()),
resolve: findOne(typeName)
},
[queryNamePlural]: {
type: paginationTC,
args: {
filter: filterInputTC,
sort: sortInputTC,
skip: `Int`,
limit: `Int`
},
resolve: findManyPaginated(typeName)
}
}).makeFieldNonNull(queryNamePlural);
};
const parseTypes = ({
doc,
plugin,
createdFrom,
schemaComposer,
parentSpan
}) => {
const types = [];
doc.definitions.forEach(def => {
const name = def.name.value;
checkIsAllowedTypeName(name);
if (schemaComposer.has(name)) {
// We don't check if ast.kind matches composer type, but rely
// that this will throw when something is wrong and get
// reported by `reportParsingError`.
// Keep the original type composer around
const typeComposer = schemaComposer.get(name); // After this, the parsed type composer will be registered as the composer
// handling the type name
const parsedType = schemaComposer.typeMapper.makeSchemaDef(def); // Merge the parsed type with the original
mergeTypes({
schemaComposer,
typeComposer,
type: parsedType,
plugin,
createdFrom,
parentSpan
}); // Set the original type composer (with the merged fields added)
// as the correct composer for the type name
schemaComposer.typeMapper.set(typeComposer.getTypeName(), typeComposer);
} else {
const parsedType = schemaComposer.typeMapper.makeSchemaDef(def);
types.push(parsedType);
}
});
return types;
};
const parseTypeDefs = ({
typeDefs,
plugin,
createdFrom,
schemaComposer,
parentSpan
}) => {
const doc = parse(typeDefs);
return parseTypes({
doc,
plugin,
createdFrom,
schemaComposer,
parentSpan
});
};
const reportParsingError = error => {
const {
message,
source,
locations
} = error;
if (source && locations && locations.length) {
const {
codeFrameColumns
} = require(`@babel/code-frame`);
const frame = codeFrameColumns(source.body, {
start: locations[0]
}, {
linesAbove: 5,
linesBelow: 5
});
report.panic(`Encountered an error parsing the provided GraphQL type definitions:\n` + message + `\n\n` + frame + `\n`);
} else {
throw error;
}
};
const stringifyArray = arr => `[${arr.map(item => Array.isArray(item) ? stringifyArray(item) : item.toString())}]`; // TODO: Import this directly from graphql-compose once we update to v7
const isNamedTypeComposer = type => type instanceof ObjectTypeComposer || type instanceof InputTypeComposer || type instanceof ScalarTypeComposer || type instanceof EnumTypeComposer || type instanceof InterfaceTypeComposer || type instanceof UnionTypeComposer;
const validate = (type, value) => {
if (type instanceof GraphQLNonNull) {
if (value == null) {
throw new Error(`Expected non-null field value.`);
}
return validate(type.ofType, value);
} else if (type instanceof GraphQLList) {
if (!Array.isArray(value)) {
throw new Error(`Expected array field value.`);
}
return value.map(v => validate(type.ofType, v));
} else {
return type.parseValue(value);
}
};
const checkQueryableInterfaces = ({
schemaComposer
}) => {
const queryableInterfaces = new Set();
schemaComposer.forEach(type => {
if (type instanceof InterfaceTypeComposer && type.getExtension(`nodeInterface`)) {
queryableInterfaces.add(type.getTypeName());
}
});
const incorrectTypes = [];
schemaComposer.forEach(type => {
if (type instanceof ObjectTypeComposer) {
const interfaces = type.getInterfaces();
if (interfaces.some(iface => queryableInterfaces.has(iface.name)) && !type.hasInterface(`Node`)) {
incorrectTypes.push(type.getTypeName());
}
}
});
if (incorrectTypes.length) {
report.panic(`Interfaces with the \`nodeInterface\` extension must only be ` + `implemented by types which also implement the \`Node\` ` + `interface. Check the type definition of ` + `${incorrectTypes.map(t => `\`${t}\``).join(`, `)}.`);
}
};
//# sourceMappingURL=schema.js.map