added tenant + graph support

This commit is contained in:
Wyatt Johnson
2018-06-16 17:21:04 -06:00
parent 02e1236792
commit 4318e1ddbe
35 changed files with 1024 additions and 269 deletions
-13
View File
@@ -1,13 +0,0 @@
import DataLoader from 'dataloader';
import {
Asset,
retrieveMany as retrieveManyAssets,
} from 'talk-server/models/asset';
import Context from 'talk-server/graph/context';
const loadAssets = async (ctx: Context, ids: string[]): Promise<Array<Asset>> =>
retrieveManyAssets(ctx.db, ids);
export default (ctx: Context) => ({
asset: new DataLoader<string, Asset>(ids => loadAssets(ctx, ids)),
});
-4
View File
@@ -1,4 +0,0 @@
import Asset from './asset';
import Context from 'talk-server/graph/context';
export default (ctx: Context) => ({ Asset: Asset(ctx) });
-5
View File
@@ -1,5 +0,0 @@
import query from './query';
export default {
Query: query,
};
@@ -1,17 +1,19 @@
import loaders from './loaders';
import { Request } from 'express';
import { Db } from 'mongodb';
import { Tenant } from 'talk-server/models/tenant';
export interface ContextOptions {
req: Request;
tenant?: Tenant;
db: Db;
}
export default class Context {
export default class TenantContext {
public loaders: ReturnType<typeof loaders>;
public db: Db;
public tenant?: Tenant;
constructor({ req, db }: ContextOptions) {
constructor({ tenant, db }: ContextOptions) {
this.tenant = tenant;
this.loaders = loaders(this);
this.db = db;
}
@@ -0,0 +1,12 @@
import DataLoader from 'dataloader';
import {
Asset,
retrieveMany as retrieveManyAssets,
} from 'talk-server/models/asset';
import Context from 'talk-server/graph/tenant/context';
export default (ctx: Context) => ({
asset: new DataLoader<string, Asset>(ids =>
retrieveManyAssets(ctx.db, ctx.tenant.id, ids)
),
});
@@ -0,0 +1,25 @@
import DataLoader from 'dataloader';
import {
Comment,
retrieveMany,
retrieveAssetConnection,
ConnectionInput,
retrieveRepliesConnection,
} from 'talk-server/models/comment';
import Context from 'talk-server/graph/tenant/context';
export default (ctx: Context) => ({
comment: new DataLoader((ids: string[]) =>
retrieveMany(ctx.db, ctx.tenant.id, ids)
),
forAsset: (assetID: string, input: ConnectionInput) =>
retrieveAssetConnection(ctx.db, ctx.tenant.id, assetID, input),
forParent: (assetID: string, parentID: string, input: ConnectionInput) =>
retrieveRepliesConnection(
ctx.db,
ctx.tenant.id,
assetID,
parentID,
input
),
});
@@ -0,0 +1,10 @@
import Assets from './assets';
import Comments from './comments';
import Users from './users';
import Context from 'talk-server/graph/tenant/context';
export default (ctx: Context) => ({
Assets: Assets(ctx),
Comments: Comments(ctx),
Users: Users(ctx),
});
@@ -0,0 +1,9 @@
import DataLoader from 'dataloader';
import { User, retrieveMany } from 'talk-server/models/user';
import Context from 'talk-server/graph/tenant/context';
export default (ctx: Context) => ({
user: new DataLoader<string, User>(ids =>
retrieveMany(ctx.db, ctx.tenant.id, ids)
),
});
@@ -0,0 +1,13 @@
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';
export default (db: Db) =>
graphqlExpress(async req => {
return {
schema,
context: new TenantContext({ db, tenant: { id: '1' } as Tenant }),
};
});
@@ -0,0 +1,8 @@
import { Asset } from 'talk-server/models/asset';
import Context from 'talk-server/graph/tenant/context';
import { ConnectionInput } from 'talk-server/models/comment';
export default {
comments: async (asset: Asset, input: ConnectionInput, ctx: Context) =>
ctx.loaders.Comments.forAsset(asset.id, input),
};
@@ -0,0 +1,9 @@
import { Comment, ConnectionInput } from 'talk-server/models/comment';
import Context from 'talk-server/graph/tenant/context';
export default {
author: async (comment: Comment, _: any, ctx: Context) =>
ctx.loaders.Users.user.load(comment.author_id),
replies: async (comment: Comment, input: ConnectionInput, ctx: Context) =>
ctx.loaders.Comments.forParent(comment.asset_id, comment.id, input),
};
@@ -0,0 +1,72 @@
import { DateTime } from 'luxon';
import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';
import { Cursor } from 'talk-server/models/connection';
function parseIntegerCursor(value: string): number {
try {
const cursor = parseInt(value);
return cursor;
} catch (err) {
return null;
}
}
function parseCursor(value: string): Cursor {
if (value.endsWith('Z')) {
const date = DateTime.fromISO(value, {});
if (!date.isValid) {
return parseIntegerCursor(value);
}
return date.toJSDate();
}
return parseIntegerCursor(value);
}
export default new GraphQLScalarType({
name: 'Cursor',
description: 'Cursor represents a paginating cursor.',
serialize(value) {
switch (typeof value) {
case 'object':
if (value instanceof Date) {
return value.toISOString();
} else if (value instanceof DateTime) {
return value.toISO();
}
return null;
case 'number':
return value.toString();
case 'string':
return value;
default:
return null;
}
},
parseValue(value) {
if (typeof value === 'string') {
return parseCursor(value);
}
return null;
},
parseLiteral(ast) {
switch (ast.kind) {
case Kind.STRING:
// This handles an empty string.
if (!ast.value || ast.value.length === 0) {
return null;
}
return parseCursor(ast.value);
case Kind.INT:
return parseIntegerCursor(ast.value);
default:
return null;
}
},
});
@@ -0,0 +1,11 @@
import Asset from './asset';
import Comment from './comment';
import Cursor from './cursor';
import Query from './query';
export default {
Asset,
Comment,
Cursor,
Query,
};
@@ -1,4 +1,4 @@
import Context from 'talk-server/graph/context';
import Context from 'talk-server/graph/tenant/context';
import { Asset } from 'talk-server/models/asset';
export default {
@@ -7,6 +7,6 @@ export default {
{ id, url }: { id?: string; url: string },
ctx: Context
): Promise<Asset> => {
return ctx.loaders.Asset.asset.load(id);
return ctx.loaders.Assets.asset.load(id);
},
};
@@ -2,11 +2,11 @@ import {
addMockFunctionsToSchema,
addResolveFunctionsToSchema,
} from 'graphql-tools';
import resolvers from 'talk-server/graph/resolvers';
import resolvers from 'talk-server/graph/tenant/resolvers';
import { getGraphQLProjectConfig } from 'graphql-config';
// Load the configuration from the provided `.graphqlconfig` file.
const config = getGraphQLProjectConfig();
const config = getGraphQLProjectConfig(__dirname, 'tenant');
// Get the GraphQLSchema from the configuration.
const schema = config.getSchema();
@@ -221,7 +221,11 @@ type Comment {
"""
replies will return the replies to this comment.
"""
replies(cursor: Cursor, limit: Int = 10): CommentsConnection
replies(
first: Int = 10
orderBy: COMMENT_SORT = CREATED_AT_DESC
after: Cursor
): CommentsConnection
}
type PageInfo {
@@ -253,7 +257,7 @@ type CommentEdge {
"""
"""
id: ID
cursor: Cursor
}
"""
@@ -276,9 +280,10 @@ type CommentsConnection {
################################################################################
enum COMMENT_SORT {
CREATED_AT
REPLIES
RESPECT
CREATED_AT_DESC
CREATED_AT_ASC
REPLIES_DESC
RESPECT_DESC
}
"""
@@ -305,7 +310,7 @@ type Asset {
"""
comments(
first: Int = 10
orderBy: COMMENT_SORT = CREATED_AT
orderBy: COMMENT_SORT = CREATED_AT_DESC
after: Cursor
): CommentsConnection
@@ -356,11 +361,13 @@ type AssetsConnection {
}
################################################################################
## Queries
## Query
################################################################################
# Query is every query possible against this GraphQL server.
type Query {
"""
comment returns a specific comment.
"""
comment(id: ID!): Comment
"""
@@ -377,6 +384,11 @@ type Query {
me is the current logged in User.
"""
me: User
"""
settings is the Settings for a given Tenant.
"""
settings: Settings!
}
################################################################################