feat: graphql schema output (#730)

* chore: refactor graphql initialization

* feat: generate graphql schema script

* feat: script commands are case insenstive
This commit is contained in:
Dan Ribbens
2022-07-08 13:50:46 -04:00
committed by GitHub
parent 2b2a562d83
commit ad43cbc808
14 changed files with 151 additions and 32 deletions

View File

@@ -0,0 +1,26 @@
/* eslint-disable no-nested-ternary */
import fs from 'fs';
import { printSchema } from 'graphql';
import Logger from '../utilities/logger';
import loadConfig from '../config/load';
import payload from '..';
export function generateGraphQLSchema(): void {
const logger = Logger();
const config = loadConfig();
payload.init({
secret: '--unused--',
mongoURL: false,
local: true,
});
logger.info('Compiling GraphQL schema...');
fs.writeFileSync(config.graphQL.schemaOutputFile, printSchema(payload.schema));
logger.info(`GraphQL written to ${config.typescript.outputFile}`);
}
// when generateGraphQLSchema.js is launched directly
if (module.id === require.main.id) {
generateGraphQLSchema();
}

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import minimist from 'minimist';
import { generateTypes } from './generateTypes';
import { generateGraphQLSchema } from './generateGraphQLSchema';
import babelConfig from '../babel.config';
require('@babel/register')({
@@ -18,7 +19,7 @@ const scriptIndex = args._.findIndex(
const script = scriptIndex === -1 ? args._[0] : args._[scriptIndex];
switch (script) {
switch (script.toLowerCase()) {
case 'build': {
build();
break;
@@ -29,6 +30,10 @@ switch (script) {
break;
}
case 'generate:graphqlschema': {
generateGraphQLSchema();
break;
}
default:
console.log(`Unknown script "${script}".`);

View File

@@ -28,6 +28,7 @@ export const defaults: Config = {
graphQL: {
maxComplexity: 1000,
disablePlaygroundInProduction: true,
schemaOutputFile: `${typeof process?.cwd === 'function' ? process.cwd() : ''}/schema.graphql`,
},
routes: {
admin: '/admin',

View File

@@ -116,6 +116,7 @@ export default joi.object({
maxComplexity: joi.number(),
disablePlaygroundInProduction: joi.boolean(),
disable: joi.boolean(),
schemaOutputFile: joi.string(),
}),
localization: joi.alternatives()
.try(

View File

@@ -62,7 +62,7 @@ export function hasTransportOptions(emailConfig: EmailOptions): emailConfig is E
export type InitOptions = {
express?: Express;
mongoURL: string;
mongoURL: string | false;
mongoOptions?: ConnectOptions;
secret: string;
email?: EmailOptions;
@@ -181,6 +181,7 @@ export type Config = {
maxComplexity?: number;
disablePlaygroundInProduction?: boolean;
disable?: boolean;
schemaOutputFile?: string;
};
components?: { [key: string]: JSX.Element | (() => JSX.Element) };
hooks?: {

View File

@@ -0,0 +1,21 @@
import { graphqlHTTP } from 'express-graphql';
import { Response } from 'express';
import { PayloadRequest } from '../express/types';
const graphQLHandler = (req: PayloadRequest, res: Response) => {
const { payload } = req;
payload.errorResponses = null;
return graphqlHTTP(
async (request, response, { variables }) => ({
schema: payload.schema,
customFormatErrorFn: payload.customFormatErrorFn,
extensions: payload.extensions,
context: { req, res },
validationRules: payload.validationRules(variables),
}),
);
};
export default graphQLHandler;

View File

@@ -1,19 +1,18 @@
/* eslint-disable no-param-reassign */
import * as GraphQL from 'graphql';
import { GraphQLObjectType, GraphQLSchema } from 'graphql';
import { graphqlHTTP } from 'express-graphql';
import queryComplexity, { fieldExtensionsEstimator, simpleEstimator } from 'graphql-query-complexity';
import errorHandler from './errorHandler';
import buildPoliciesType from './schema/buildPoliciesType';
import { Payload } from '..';
import buildLocaleInputType from './schema/buildLocaleInputType';
import buildFallbackLocaleInputType from './schema/buildFallbackLocaleInputType';
import initCollections from '../collections/graphql/init';
import initGlobals from '../globals/graphql/init';
import initPreferences from '../preferences/graphql/init';
import buildPoliciesType from './schema/buildPoliciesType';
import accessResolver from '../auth/graphql/resolvers/access';
import errorHandler from './errorHandler';
const initializeGraphQL = (req, res) => {
const { payload } = req;
export default function registerSchema(payload: Payload): void {
payload.types = {
blockTypes: {},
blockInputTypes: {},
@@ -106,18 +105,4 @@ const initializeGraphQL = (req, res) => {
// onComplete: (complexity) => { console.log('Query Complexity:', complexity); },
}),
]);
payload.errorResponses = null;
return graphqlHTTP(
async (request, response, { variables }) => ({
schema: payload.schema,
customFormatErrorFn: payload.customFormatErrorFn,
extensions: payload.extensions,
context: { req, res },
validationRules: payload.validationRules(variables),
}),
);
};
export default initializeGraphQL;
}

View File

@@ -1,4 +1,4 @@
import express, { Express, Router } from 'express';
import express, { Express, Response, Router } from 'express';
import pino from 'pino';
import crypto from 'crypto';
import { GraphQLError, GraphQLFormattedError, GraphQLSchema } from 'graphql';
@@ -29,7 +29,8 @@ import initGlobals from './globals/init';
import { Globals, TypeWithID as GlobalTypeWithID } from './globals/config/types';
import initGraphQLPlayground from './graphql/initPlayground';
import initStatic from './express/static';
import initializeGraphQL from './graphql';
import registerSchema from './graphql/registerSchema';
import graphQLHandler from './graphql/graphQLHandler';
import buildEmail from './email/build';
import identifyAPI from './express/middleware/identifyAPI';
import errorHandler, { ErrorHandler } from './express/middleware/errorHandler';
@@ -98,7 +99,7 @@ export class Payload {
secret: string;
mongoURL: string;
mongoURL: string | false;
local: boolean;
@@ -113,8 +114,8 @@ export class Payload {
types: {
blockTypes: any;
blockInputTypes: any;
localeInputType: any;
fallbackLocaleInputType: any;
localeInputType?: any;
fallbackLocaleInputType?: any;
};
Query: { name: string; fields: { [key: string]: any } } = { name: 'Query', fields: {} };
@@ -146,7 +147,7 @@ export class Payload {
);
}
if (!options.mongoURL) {
if (options.mongoURL !== false && typeof options.mongoURL !== 'string') {
throw new Error('Error: missing MongoDB connection URL.');
}
@@ -178,8 +179,13 @@ export class Payload {
initGlobals(this);
// Connect to database
connectMongoose(this.mongoURL, options.mongoOptions, options.local, this.logger);
if (this.mongoURL) {
connectMongoose(this.mongoURL, options.mongoOptions, options.local, this.logger);
}
if (!this.config.graphQL.disable) {
registerSchema(this);
}
// If not initializing locally, set up HTTP routing
if (!this.local) {
options.express.use((req: PayloadRequest, res, next) => {
@@ -202,7 +208,7 @@ export class Payload {
this.router.use(
this.config.routes.graphQL,
identifyAPI('GraphQL'),
(req, res) => initializeGraphQL(req, res)(req, res),
(req: PayloadRequest, res: Response) => graphQLHandler(req, res)(req, res),
);
initGraphQLPlayground(this);
}