mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
feat: upgraded apollo-server-express, logging extension (#2058)
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import uuid from "uuid";
|
||||
|
||||
import { Config } from "talk-common/config";
|
||||
import logger from "talk-server/logger";
|
||||
import { User } from "talk-server/models/user";
|
||||
import { Request } from "talk-server/types/express";
|
||||
|
||||
@@ -9,9 +12,14 @@ export interface CommonContextOptions {
|
||||
}
|
||||
|
||||
export default class CommonContext {
|
||||
public user?: User;
|
||||
public req?: Request;
|
||||
public config: Config;
|
||||
public readonly user?: User;
|
||||
public readonly req?: Request;
|
||||
public readonly config: Config;
|
||||
|
||||
public readonly logger = logger.child({
|
||||
context: "graph",
|
||||
contextID: uuid.v4(),
|
||||
});
|
||||
|
||||
constructor({ user, req, config }: CommonContextOptions) {
|
||||
this.user = user;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { formatApolloErrors } from "apollo-server-errors";
|
||||
import { GraphQLError } from "graphql";
|
||||
import { GraphQLExtension, GraphQLResponse } from "graphql-extensions";
|
||||
|
||||
import CommonContext from "talk-server/graph/common/context";
|
||||
|
||||
export class LoggerExtension implements GraphQLExtension<CommonContext> {
|
||||
private logError = (ctx: CommonContext) => (err: Error) => {
|
||||
if (err instanceof GraphQLError) {
|
||||
ctx.logger.error({ err: err.originalError }, "graphql error");
|
||||
} else {
|
||||
ctx.logger.error({ err }, "graphql query error");
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
public requestDidStart(o: {
|
||||
operationName?: string;
|
||||
context: CommonContext;
|
||||
}) {
|
||||
o.context.logger.debug({ operationName: o.operationName }, "graphql query");
|
||||
}
|
||||
|
||||
public willSendResponse(o: {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: CommonContext;
|
||||
}): void | { graphqlResponse: GraphQLResponse; context: CommonContext } {
|
||||
if (o.graphqlResponse.errors) {
|
||||
return {
|
||||
...o,
|
||||
graphqlResponse: {
|
||||
...o.graphqlResponse,
|
||||
errors: formatApolloErrors(o.graphqlResponse.errors, {
|
||||
formatter: this.logError(o.context),
|
||||
debug: false,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
import { resolveGraphqlOptions } from "apollo-server-core";
|
||||
import { GraphQLOptions } from "apollo-server-express";
|
||||
import { Handler } from "express";
|
||||
import { FieldDefinitionNode, GraphQLError, ValidationContext } from "graphql";
|
||||
|
||||
// TODO: when https://github.com/apollographql/apollo-server/pull/1907 is merged, update this import path
|
||||
import {
|
||||
ExpressGraphQLOptionsFunction,
|
||||
graphqlExpress,
|
||||
GraphQLOptions,
|
||||
} from "apollo-server-express";
|
||||
import { FieldDefinitionNode, GraphQLError, ValidationContext } from "graphql";
|
||||
} from "apollo-server-express/dist/expressApollo";
|
||||
|
||||
import { Config } from "talk-common/config";
|
||||
import { Omit } from "talk-common/types";
|
||||
import { LoggerExtension } from "talk-server/graph/common/middleware/extensions/logger";
|
||||
|
||||
// Sourced from: https://github.com/apollographql/apollo-server/blob/958846887598491fadea57b3f9373d129300f250/packages/apollo-server-core/src/ApolloServer.ts#L46-L57
|
||||
const NoIntrospection = (context: ValidationContext) => ({
|
||||
@@ -23,26 +28,33 @@ const NoIntrospection = (context: ValidationContext) => ({
|
||||
|
||||
export const graphqlMiddleware = (
|
||||
config: Config,
|
||||
baseOptions: GraphQLOptions | ExpressGraphQLOptionsFunction
|
||||
) => {
|
||||
// Generate the validation rules.
|
||||
const validationRules: Array<(context: ValidationContext) => any> = [];
|
||||
requestOptions: ExpressGraphQLOptionsFunction
|
||||
): Handler => {
|
||||
// Create a new baseOptions that will be merged into the new options.
|
||||
const baseOptions: Omit<GraphQLOptions, "schema"> = {
|
||||
// Disable the debug mode, as we already add in our logging function.
|
||||
debug: false,
|
||||
// Include extensions.
|
||||
extensions: [
|
||||
// Log queries and errors.
|
||||
() => new LoggerExtension(),
|
||||
],
|
||||
};
|
||||
|
||||
if (config.get("env") === "production" && !config.get("enable_graphiql")) {
|
||||
// Disable introspection in production.
|
||||
validationRules.push(NoIntrospection);
|
||||
baseOptions.validationRules = [NoIntrospection];
|
||||
}
|
||||
|
||||
// Generate the actual middleware.
|
||||
return graphqlExpress(async (req, res) => {
|
||||
// Resolve the base options.
|
||||
const requestOptions = await resolveGraphqlOptions(baseOptions, req, res);
|
||||
// Resolve the options for the GraphQL middleware.
|
||||
const options = await requestOptions(req, res);
|
||||
|
||||
// Apply the validators, sourced from: https://github.com/apollographql/apollo-server/blob/958846887598491fadea57b3f9373d129300f250/packages/apollo-server-core/src/ApolloServer.ts#L104-L107
|
||||
requestOptions.validationRules = requestOptions.validationRules
|
||||
? requestOptions.validationRules.concat(validationRules)
|
||||
: validationRules;
|
||||
|
||||
return requestOptions;
|
||||
// Provide the options.
|
||||
return {
|
||||
...options,
|
||||
...baseOptions,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface ManagementContextOptions {
|
||||
}
|
||||
|
||||
export default class ManagementContext extends CommonContext {
|
||||
public mongo: Db;
|
||||
public readonly mongo: Db;
|
||||
|
||||
constructor({ req, mongo, config }: ManagementContextOptions) {
|
||||
super({ req, config });
|
||||
|
||||
@@ -24,14 +24,14 @@ export interface TenantContextOptions {
|
||||
}
|
||||
|
||||
export default class TenantContext extends CommonContext {
|
||||
public tenant: Tenant;
|
||||
public tenantCache: TenantCache;
|
||||
public user?: User;
|
||||
public mongo: Db;
|
||||
public redis: Redis;
|
||||
public queue: TaskQueue;
|
||||
public loaders: ReturnType<typeof loaders>;
|
||||
public mutators: ReturnType<typeof mutators>;
|
||||
public readonly tenant: Tenant;
|
||||
public readonly tenantCache: TenantCache;
|
||||
public readonly user?: User;
|
||||
public readonly mongo: Db;
|
||||
public readonly redis: Redis;
|
||||
public readonly queue: TaskQueue;
|
||||
public readonly loaders: ReturnType<typeof loaders>;
|
||||
public readonly mutators: ReturnType<typeof mutators>;
|
||||
|
||||
constructor({
|
||||
req,
|
||||
|
||||
Reference in New Issue
Block a user