mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
initial subscription support
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
graphqlExpress,
|
||||
ExpressGraphQLOptionsFunction,
|
||||
GraphQLOptions,
|
||||
} from 'apollo-server-express';
|
||||
import { GraphQLError, FieldDefinitionNode, ValidationContext } from 'graphql';
|
||||
import { resolveGraphqlOptions } from 'apollo-server-core';
|
||||
import { Config } from 'talk-server/config';
|
||||
|
||||
// Sourced from: https://github.com/apollographql/apollo-server/blob/958846887598491fadea57b3f9373d129300f250/packages/apollo-server-core/src/ApolloServer.ts#L46-L57
|
||||
const NoIntrospection = (context: ValidationContext) => ({
|
||||
Field(node: FieldDefinitionNode) {
|
||||
if (node.name.value === '__schema' || node.name.value === '__type') {
|
||||
context.reportError(
|
||||
new GraphQLError(
|
||||
'GraphQL introspection is not allowed in production, but the query contained __schema or __type.',
|
||||
[node]
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const graphqlMiddleware = (
|
||||
config: Config,
|
||||
baseOptions: GraphQLOptions | ExpressGraphQLOptionsFunction
|
||||
) => {
|
||||
// Generate the validation rules.
|
||||
const validationRules: Array<(context: ValidationContext) => any> = [];
|
||||
|
||||
if (config.get('env') !== 'production') {
|
||||
// Disable introspection in production.
|
||||
validationRules.push(NoIntrospection);
|
||||
}
|
||||
|
||||
// Generate the actual middleware.
|
||||
return graphqlExpress(async (req, res) => {
|
||||
// Resolve the base options.
|
||||
const requestOptions = await resolveGraphqlOptions(
|
||||
baseOptions,
|
||||
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;
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { addResolveFunctionsToSchema, IResolvers } from 'graphql-tools';
|
||||
import { getGraphQLProjectConfig } from 'graphql-config';
|
||||
|
||||
export default function loadSchema(projectName: string, resolvers: IResolvers) {
|
||||
// Load the configuration from the provided `.graphqlconfig` file.
|
||||
const config = getGraphQLProjectConfig(__dirname, projectName);
|
||||
|
||||
// Get the GraphQLSchema from the configuration.
|
||||
const schema = config.getSchema();
|
||||
|
||||
// Attach the resolvers to the schema.
|
||||
addResolveFunctionsToSchema({ schema, resolvers });
|
||||
|
||||
return schema;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import http from 'http';
|
||||
import { SubscriptionServer } from 'subscriptions-transport-ws';
|
||||
import { GraphQLSchema, execute, subscribe } from 'graphql';
|
||||
|
||||
export interface SubscriptionMiddlewareOptions {
|
||||
schema: GraphQLSchema;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export function handleSubscriptions(
|
||||
server: http.Server,
|
||||
{ schema, path }: SubscriptionMiddlewareOptions
|
||||
): SubscriptionServer {
|
||||
// Configure some options for the subscription system.
|
||||
const options = {
|
||||
schema,
|
||||
execute,
|
||||
subscribe,
|
||||
};
|
||||
|
||||
// Configure the socket options for the websocket server. It needs to handle
|
||||
// upgrade requests on that route.
|
||||
const socketOption = {
|
||||
server,
|
||||
path,
|
||||
};
|
||||
|
||||
return new SubscriptionServer(options, socketOption);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { RedisPubSub } from 'graphql-redis-subscriptions';
|
||||
import { createRedisClient } from 'talk-server/services/redis';
|
||||
import { Config } from 'talk-server/config';
|
||||
|
||||
export async function createPubSub(config: Config): Promise<RedisPubSub> {
|
||||
// Create the Redis clients for the PubSub server.
|
||||
const publisher = await createRedisClient(config);
|
||||
const subscriber = await createRedisClient(config);
|
||||
|
||||
// Create the new PubSub manager.
|
||||
return new RedisPubSub({
|
||||
publisher,
|
||||
subscriber,
|
||||
});
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { graphqlExpress } from 'apollo-server-express';
|
||||
import schema from './schema';
|
||||
import Context from './context';
|
||||
import { Db } from 'mongodb';
|
||||
import { GraphQLSchema } from 'graphql';
|
||||
|
||||
export default (db: Db) =>
|
||||
graphqlExpress(async req => {
|
||||
return {
|
||||
schema,
|
||||
context: new Context({ db }),
|
||||
};
|
||||
});
|
||||
import { graphqlMiddleware } from 'talk-server/graph/common/middleware';
|
||||
import { Config } from 'talk-server/config';
|
||||
|
||||
import Context from './context';
|
||||
|
||||
export default (schema: GraphQLSchema, config: Config, db: Db) =>
|
||||
graphqlMiddleware(config, async () => ({
|
||||
schema,
|
||||
context: new Context({ db }),
|
||||
}));
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { addResolveFunctionsToSchema } from 'graphql-tools';
|
||||
import { getGraphQLProjectConfig } from 'graphql-config';
|
||||
import loadSchema from 'talk-server/graph/common/schema';
|
||||
import resolvers from 'talk-server/graph/management/resolvers';
|
||||
|
||||
import resolvers from '../resolvers';
|
||||
|
||||
// Load the configuration from the provided `.graphqlconfig` file.
|
||||
const config = getGraphQLProjectConfig(__dirname, 'management');
|
||||
|
||||
// Get the GraphQLSchema from the configuration.
|
||||
const schema = config.getSchema();
|
||||
|
||||
// Attach the resolvers to the schema.
|
||||
addResolveFunctionsToSchema({ schema, resolvers });
|
||||
|
||||
export default schema;
|
||||
export default function getManagementSchema() {
|
||||
return loadSchema('management', resolvers);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { GraphQLSchema } from 'graphql';
|
||||
|
||||
export interface Schemas {
|
||||
management: GraphQLSchema;
|
||||
tenant: GraphQLSchema;
|
||||
}
|
||||
@@ -1,13 +1,24 @@
|
||||
import { graphqlExpress } from 'apollo-server-express';
|
||||
import schema from './schema';
|
||||
import TenantContext from './context';
|
||||
import { Db } from 'mongodb';
|
||||
import { Tenant } from 'talk-server/models/tenant';
|
||||
import { GraphQLSchema } from 'graphql';
|
||||
|
||||
import { retrieveByDomain } from 'talk-server/models/tenant';
|
||||
import { createPubSub } from 'talk-server/graph/common/subscriptions/pubsub';
|
||||
import { Config } from 'talk-server/config';
|
||||
import { graphqlMiddleware } from 'talk-server/graph/common/middleware';
|
||||
|
||||
import TenantContext from './context';
|
||||
|
||||
export default async (schema: GraphQLSchema, config: Config, db: Db) => {
|
||||
// Configure the PubSub broker.
|
||||
const pubsub = await createPubSub(config);
|
||||
|
||||
return graphqlMiddleware(config, async req => {
|
||||
// TODO: replace with shared synced cache instead of direct db access.
|
||||
const tenant = await retrieveByDomain(db, req.hostname);
|
||||
|
||||
export default (db: Db) =>
|
||||
graphqlExpress(async req => {
|
||||
return {
|
||||
schema,
|
||||
context: new TenantContext({ db, tenant: { id: '1' } as Tenant }),
|
||||
context: new TenantContext({ db, tenant }),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import Context from 'talk-server/graph/tenant/context';
|
||||
import TenantContext from 'talk-server/graph/tenant/context';
|
||||
import { Asset } from 'talk-server/models/asset';
|
||||
|
||||
export default {
|
||||
asset: async (
|
||||
_: any,
|
||||
{ id, url }: { id?: string; url: string },
|
||||
ctx: Context
|
||||
ctx: TenantContext
|
||||
): Promise<Asset> => {
|
||||
return ctx.loaders.Assets.asset.load(id);
|
||||
},
|
||||
settings: async (parent: any, args: any, ctx: TenantContext) => ctx.tenant,
|
||||
};
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { addResolveFunctionsToSchema } from 'graphql-tools';
|
||||
import { getGraphQLProjectConfig } from 'graphql-config';
|
||||
import loadSchema from 'talk-server/graph/common/schema';
|
||||
import resolvers from 'talk-server/graph/tenant/resolvers';
|
||||
|
||||
import resolvers from '../resolvers';
|
||||
|
||||
// Load the configuration from the provided `.graphqlconfig` file.
|
||||
const config = getGraphQLProjectConfig(__dirname, 'tenant');
|
||||
|
||||
// Get the GraphQLSchema from the configuration.
|
||||
const schema = config.getSchema();
|
||||
|
||||
// Attach the resolvers to the schema.
|
||||
addResolveFunctionsToSchema({ schema, resolvers });
|
||||
|
||||
export default schema;
|
||||
export default function getTenantSchema() {
|
||||
return loadSchema('tenant', resolvers);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user