mirror of
https://github.com/wassname/talk.git
synced 2026-07-13 17:45:56 +08:00
added tenant + graph support
This commit is contained in:
@@ -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)),
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
import Asset from './asset';
|
||||
import Context from 'talk-server/graph/context';
|
||||
|
||||
export default (ctx: Context) => ({ Asset: Asset(ctx) });
|
||||
@@ -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,
|
||||
};
|
||||
+2
-2
@@ -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
-2
@@ -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();
|
||||
+20
-8
@@ -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!
|
||||
}
|
||||
|
||||
################################################################################
|
||||
Reference in New Issue
Block a user