mirror of
https://github.com/wassname/talk.git
synced 2026-08-11 11:27:10 +08:00
feature: added generated graph types
This commit is contained in:
Generated
+9
@@ -9125,6 +9125,15 @@
|
||||
"cross-fetch": "2.0.0"
|
||||
}
|
||||
},
|
||||
"graphql-schema-typescript": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/graphql-schema-typescript/-/graphql-schema-typescript-1.2.1.tgz",
|
||||
"integrity": "sha512-ipZh3Epm/Kqcy6MF5FM6uxwCMFok07q+6qyxFOa7ViRufcjzH9Y3nECmECH5WgqRGl2wR6TmskbZd5qJJrGpoA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yargs": "^11.0.0"
|
||||
}
|
||||
},
|
||||
"graphql-subscriptions": {
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-0.5.8.tgz",
|
||||
|
||||
+3
-1
@@ -20,7 +20,8 @@
|
||||
"lint": "npm-run-all --parallel lint:*",
|
||||
"lint:server": "tslint --project ./tsconfig.json",
|
||||
"lint:client": "tslint --project ./src/core/client/tsconfig.json",
|
||||
"docz:watch": "docz dev"
|
||||
"docz:watch": "docz dev",
|
||||
"postinstall": "node ./scripts/types.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
@@ -95,6 +96,7 @@
|
||||
"fluent-langneg": "^0.1.0",
|
||||
"fluent-react": "^0.7.0",
|
||||
"graphql-playground-middleware-express": "^1.7.0",
|
||||
"graphql-schema-typescript": "^1.2.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"jest": "^23.2.0",
|
||||
"loader-utils": "^1.1.0",
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
const { Linter, Configuration } = require("tslint");
|
||||
const { generateTSTypesAsString } = require("graphql-schema-typescript");
|
||||
const { getGraphQLConfig } = require("graphql-config");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
function lint(files) {
|
||||
const linter = new Linter({ fix: true });
|
||||
|
||||
for (const { fileName, types } of files) {
|
||||
const configuration = Configuration.findConfiguration(null, fileName)
|
||||
.results;
|
||||
linter.lint(fileName, types, configuration);
|
||||
}
|
||||
}
|
||||
|
||||
function getFileName(name) {
|
||||
return path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"src",
|
||||
"core",
|
||||
"server",
|
||||
"graph",
|
||||
name,
|
||||
"schema",
|
||||
"__generated__",
|
||||
"types.ts"
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const config = getGraphQLConfig(__dirname);
|
||||
const projects = config.getProjects();
|
||||
|
||||
const files = [
|
||||
{
|
||||
name: "tenant",
|
||||
fileName: getFileName("tenant"),
|
||||
config: {
|
||||
contextType: "TenantContext",
|
||||
importStatements: [
|
||||
'import { Cursor } from "talk-server/models/connection";',
|
||||
'import TenantContext from "talk-server/graph/tenant/context";',
|
||||
],
|
||||
customScalarType: { Cursor: "Cursor", Time: "string" },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "management",
|
||||
fileName: getFileName("management"),
|
||||
config: {
|
||||
contextType: "ManagementContext",
|
||||
importStatements: [
|
||||
'import ManagementContext from "talk-server/graph/management/context";',
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const file of files) {
|
||||
// Load the graph schema.
|
||||
const schema = projects[file.name].getSchema();
|
||||
|
||||
// Create the generated directory.
|
||||
const dir = path.dirname(file.fileName);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
|
||||
// Create the types for this file.
|
||||
file.types = await generateTSTypesAsString(schema, {
|
||||
tabSpaces: 2,
|
||||
typePrefix: "GQL",
|
||||
contextType: "any",
|
||||
strictNulls: false,
|
||||
minimizeInterfaceImplementation: true,
|
||||
...file.config,
|
||||
});
|
||||
}
|
||||
|
||||
// Send the files off to the linter to be linted and written.
|
||||
lint(files);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
main()
|
||||
.then(files => {
|
||||
for (const { fileName } of files) {
|
||||
console.log(`Generated ${fileName}`);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import Cursor from "../../common/scalars/cursor";
|
||||
import { GQLResolver } from "talk-server/graph/management/schema/__generated__/types";
|
||||
|
||||
export default {
|
||||
Cursor,
|
||||
};
|
||||
const Resolvers: GQLResolver = {};
|
||||
|
||||
export default Resolvers;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { IResolvers } from "graphql-tools";
|
||||
|
||||
import loadSchema from "talk-server/graph/common/schema";
|
||||
import resolvers from "talk-server/graph/management/resolvers";
|
||||
|
||||
export default function getManagementSchema() {
|
||||
return loadSchema("management", resolvers);
|
||||
return loadSchema("management", resolvers as IResolvers);
|
||||
}
|
||||
|
||||
@@ -7,27 +7,22 @@ Time represented as an ISO8601 string.
|
||||
"""
|
||||
scalar Time
|
||||
|
||||
"""
|
||||
Cursor represents a paginating cursor.
|
||||
"""
|
||||
scalar Cursor
|
||||
|
||||
################################################################################
|
||||
## Tenant
|
||||
################################################################################
|
||||
|
||||
type Tenant {
|
||||
id: ID!
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
organizationName is the name of the organization.
|
||||
"""
|
||||
organizationName: String
|
||||
"""
|
||||
organizationName is the name of the organization.
|
||||
"""
|
||||
organizationName: String
|
||||
|
||||
"""
|
||||
organizationContactEmail is the email of the organization.
|
||||
"""
|
||||
organizationContactEmail: String
|
||||
"""
|
||||
organizationContactEmail is the email of the organization.
|
||||
"""
|
||||
organizationContactEmail: String
|
||||
}
|
||||
|
||||
################################################################################
|
||||
@@ -35,5 +30,5 @@ type Tenant {
|
||||
################################################################################
|
||||
|
||||
type Query {
|
||||
tenant(id: ID!): Tenant
|
||||
tenant(id: ID!): Tenant
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import DataLoader from "dataloader";
|
||||
|
||||
import Context from "talk-server/graph/tenant/context";
|
||||
import {
|
||||
ConnectionInput,
|
||||
AssetToCommentsArgs,
|
||||
CommentToRepliesArgs,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import {
|
||||
retrieveAssetConnection,
|
||||
retrieveMany,
|
||||
retrieveRepliesConnection,
|
||||
@@ -11,8 +15,8 @@ export default (ctx: Context) => ({
|
||||
comment: new DataLoader((ids: string[]) =>
|
||||
retrieveMany(ctx.db, ctx.tenant.id, ids)
|
||||
),
|
||||
forAsset: (assetID: string, input: ConnectionInput) =>
|
||||
forAsset: (assetID: string, input: AssetToCommentsArgs) =>
|
||||
retrieveAssetConnection(ctx.db, ctx.tenant.id, assetID, input),
|
||||
forParent: (assetID: string, parentID: string, input: ConnectionInput) =>
|
||||
forParent: (assetID: string, parentID: string, input: CommentToRepliesArgs) =>
|
||||
retrieveRepliesConnection(ctx.db, ctx.tenant.id, assetID, parentID, input),
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import TenantContext from "talk-server/graph/tenant/context";
|
||||
import { CreateCommentInput } from "talk-server/graph/tenant/resolvers/mutation";
|
||||
import { GQLCreateCommentInput } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { Comment } from "talk-server/models/comment";
|
||||
import { create } from "talk-server/services/comments";
|
||||
|
||||
export default (ctx: TenantContext) => ({
|
||||
create: (input: CreateCommentInput): Promise<Comment> => {
|
||||
create: (input: GQLCreateCommentInput): Promise<Comment> => {
|
||||
// FIXME: remove tenant + user !
|
||||
return create(ctx.db, ctx.tenant!.id, {
|
||||
return create(ctx.db, ctx.tenant.id, {
|
||||
author_id: ctx.user!.id,
|
||||
asset_id: input.assetID,
|
||||
body: input.body,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import Context from "talk-server/graph/tenant/context";
|
||||
import { GQLAssetTypeResolver } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { Asset } from "talk-server/models/asset";
|
||||
import { ConnectionInput } from "talk-server/models/comment";
|
||||
|
||||
export default {
|
||||
comments: async (asset: Asset, input: ConnectionInput, ctx: Context) =>
|
||||
const Asset: GQLAssetTypeResolver<Asset> = {
|
||||
comments: (asset, input, ctx) =>
|
||||
ctx.loaders.Comments.forAsset(asset.id, input),
|
||||
// TODO: implement this.
|
||||
isClosed: () => false,
|
||||
};
|
||||
|
||||
export default Asset;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import Context from "talk-server/graph/tenant/context";
|
||||
import { Comment, ConnectionInput } from "talk-server/models/comment";
|
||||
import { GQLCommentTypeResolver } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { Comment } from "talk-server/models/comment";
|
||||
|
||||
export default {
|
||||
author: async (comment: Comment, _: any, ctx: Context) =>
|
||||
const Comment: GQLCommentTypeResolver<Comment> = {
|
||||
author: async (comment, args, ctx) =>
|
||||
ctx.loaders.Users.user.load(comment.author_id),
|
||||
replies: async (comment: Comment, input: ConnectionInput, ctx: Context) =>
|
||||
replies: async (comment, input, ctx) =>
|
||||
ctx.loaders.Comments.forParent(comment.asset_id, comment.id, input),
|
||||
};
|
||||
|
||||
export default Comment;
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import Cursor from "../../common/scalars/cursor";
|
||||
import Cursor from "talk-server/graph/common/scalars/cursor";
|
||||
import { GQLResolver } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import Asset from "./asset";
|
||||
import Comment from "./comment";
|
||||
import Mutation from "./mutation";
|
||||
import Query from "./query";
|
||||
|
||||
export default {
|
||||
const Resolvers: GQLResolver = {
|
||||
Asset,
|
||||
Comment,
|
||||
Cursor,
|
||||
Query,
|
||||
Mutation,
|
||||
};
|
||||
|
||||
export default Resolvers;
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
import { ClientMutationProps } from "talk-server/graph/common/resolvers/mutation";
|
||||
import TenantContext from "talk-server/graph/tenant/context";
|
||||
import { Comment } from "talk-server/models/comment";
|
||||
import { GQLMutationTypeResolver } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
export interface CreateCommentInput extends ClientMutationProps {
|
||||
assetID: string;
|
||||
parentID?: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface CreateCommentPayload extends ClientMutationProps {
|
||||
comment: Comment;
|
||||
}
|
||||
|
||||
const Mutation = {
|
||||
createComment: async (
|
||||
source: void,
|
||||
input: CreateCommentInput,
|
||||
ctx: TenantContext
|
||||
): Promise<CreateCommentPayload> => ({
|
||||
const Mutation: GQLMutationTypeResolver<void> = {
|
||||
createComment: async (source, { input }, ctx) => ({
|
||||
comment: await ctx.mutators.Comment.create(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import TenantContext from "talk-server/graph/tenant/context";
|
||||
import { GQLQueryTypeResolver } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
export default {
|
||||
asset: async (
|
||||
source: void,
|
||||
{ id }: { id: string; url: string },
|
||||
ctx: TenantContext
|
||||
) => ctx.loaders.Assets.asset.load(id),
|
||||
settings: async (parent: any, args: any, ctx: TenantContext) => ctx.tenant,
|
||||
const Query: GQLQueryTypeResolver<void> = {
|
||||
asset: (source, args, ctx) => ctx.loaders.Assets.asset.load(args.id),
|
||||
settings: (parent, args, ctx) => ctx.tenant,
|
||||
};
|
||||
|
||||
export default Query;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { IResolvers } from "graphql-tools";
|
||||
|
||||
import loadSchema from "talk-server/graph/common/schema";
|
||||
import resolvers from "talk-server/graph/tenant/resolvers";
|
||||
|
||||
export default function getTenantSchema() {
|
||||
return loadSchema("tenant", resolvers);
|
||||
return loadSchema("tenant", resolvers as IResolvers);
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ enum MODERATION_MODE {
|
||||
}
|
||||
|
||||
"""
|
||||
Wordlist describes all the available wordlists.
|
||||
WordlistSettings describes all the available wordlists.
|
||||
"""
|
||||
type Wordlist {
|
||||
type WordlistSettings {
|
||||
"""
|
||||
banned words will by default reject the comment if it is found.
|
||||
"""
|
||||
@@ -46,7 +46,20 @@ type Wordlist {
|
||||
}
|
||||
|
||||
# Settings stores the global settings for a given installation.
|
||||
|
||||
################################################################################
|
||||
## Settings
|
||||
################################################################################
|
||||
|
||||
"""
|
||||
Settings stores the global settings for a given Tenant.
|
||||
"""
|
||||
type Settings {
|
||||
"""
|
||||
domain is the domain that is associated with this Tenant.
|
||||
"""
|
||||
domain: String!
|
||||
|
||||
"""
|
||||
moderation is the moderation mode for all Asset's on the site.
|
||||
"""
|
||||
@@ -152,7 +165,7 @@ type Settings {
|
||||
"""
|
||||
wordlist will return a given list of words.
|
||||
"""
|
||||
wordlist: Wordlist!
|
||||
wordlist: WordlistSettings!
|
||||
|
||||
"""
|
||||
domains will return a given list of whitelisted domains.
|
||||
@@ -222,8 +235,8 @@ type Comment {
|
||||
replies will return the replies to this comment.
|
||||
"""
|
||||
replies(
|
||||
first: Int = 10
|
||||
orderBy: COMMENT_SORT = CREATED_AT_DESC
|
||||
first: Int! = 10
|
||||
orderBy: COMMENT_SORT! = CREATED_AT_DESC
|
||||
after: Cursor
|
||||
): CommentsConnection
|
||||
}
|
||||
@@ -299,8 +312,8 @@ type Asset {
|
||||
comments are the comments on the Asset.
|
||||
"""
|
||||
comments(
|
||||
first: Int = 10
|
||||
orderBy: COMMENT_SORT = CREATED_AT_DESC
|
||||
first: Int! = 10
|
||||
orderBy: COMMENT_SORT! = CREATED_AT_DESC
|
||||
after: Cursor
|
||||
): CommentsConnection
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import bunyan from "bunyan";
|
||||
|
||||
const logger = bunyan.createLogger({ name: "talk" });
|
||||
const logger = bunyan.createLogger({
|
||||
name: "talk",
|
||||
serializers: bunyan.stdSerializers,
|
||||
});
|
||||
|
||||
export default logger;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { merge } from "lodash";
|
||||
import { Db } from "mongodb";
|
||||
import uuid from "uuid";
|
||||
|
||||
import { Omit, Sub } from "talk-common/types";
|
||||
import { GQLCOMMENT_SORT } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { ActionCounts } from "talk-server/models/actions";
|
||||
import { Connection, Cursor } from "talk-server/models/connection";
|
||||
import Query from "talk-server/models/query";
|
||||
import { TenantResource } from "talk-server/models/tenant";
|
||||
import uuid from "uuid";
|
||||
|
||||
function collection(db: Db) {
|
||||
return db.collection<Readonly<Comment>>("comments");
|
||||
@@ -121,16 +123,9 @@ export async function retrieveMany(db: Db, tenantID: string, ids: string[]) {
|
||||
return ids.map(id => comments.find(comment => comment.id === id) || null);
|
||||
}
|
||||
|
||||
export enum CommentSort {
|
||||
CREATED_AT_DESC = "CREATED_AT_DESC",
|
||||
CREATED_AT_ASC = "CREATED_AT_ASC",
|
||||
REPLIES_DESC = "REPLIES_DESC",
|
||||
RESPECT_DESC = "RESPECT_DESC",
|
||||
}
|
||||
|
||||
export interface ConnectionInput {
|
||||
first: number;
|
||||
orderBy: CommentSort;
|
||||
orderBy: GQLCOMMENT_SORT;
|
||||
after?: Cursor;
|
||||
}
|
||||
|
||||
@@ -144,12 +139,12 @@ export interface ConnectionInput {
|
||||
function nodesToEdge(input: ConnectionInput, nodes: Comment[]) {
|
||||
let getCursor: (comment: Comment, index: number) => Cursor;
|
||||
switch (input.orderBy) {
|
||||
case CommentSort.CREATED_AT_DESC:
|
||||
case CommentSort.CREATED_AT_ASC:
|
||||
case GQLCOMMENT_SORT.CREATED_AT_DESC:
|
||||
case GQLCOMMENT_SORT.CREATED_AT_ASC:
|
||||
getCursor = comment => comment.created_at;
|
||||
break;
|
||||
case CommentSort.REPLIES_DESC:
|
||||
case CommentSort.RESPECT_DESC:
|
||||
case GQLCOMMENT_SORT.REPLIES_DESC:
|
||||
case GQLCOMMENT_SORT.RESPECT_DESC:
|
||||
getCursor = (_, index) =>
|
||||
(input.after ? (input.after as number) : 0) + index + 1;
|
||||
break;
|
||||
@@ -226,25 +221,25 @@ async function retrieveConnection(
|
||||
) {
|
||||
// Apply some sorting options.
|
||||
switch (input.orderBy) {
|
||||
case CommentSort.CREATED_AT_DESC:
|
||||
case GQLCOMMENT_SORT.CREATED_AT_DESC:
|
||||
query.orderBy({ created_at: -1 });
|
||||
if (input.after) {
|
||||
query.where({ created_at: { $lt: input.after as Date } });
|
||||
}
|
||||
break;
|
||||
case CommentSort.CREATED_AT_ASC:
|
||||
case GQLCOMMENT_SORT.CREATED_AT_ASC:
|
||||
query.orderBy({ created_at: 1 });
|
||||
if (input.after) {
|
||||
query.where({ created_at: { $gt: input.after as Date } });
|
||||
}
|
||||
break;
|
||||
case CommentSort.REPLIES_DESC:
|
||||
case GQLCOMMENT_SORT.REPLIES_DESC:
|
||||
query.orderBy({ reply_count: -1, created_at: -1 });
|
||||
if (input.after) {
|
||||
query.after(input.after as number);
|
||||
}
|
||||
break;
|
||||
case CommentSort.RESPECT_DESC:
|
||||
case GQLCOMMENT_SORT.RESPECT_DESC:
|
||||
query.orderBy({ "action_counts.respect": -1, created_at: -1 });
|
||||
if (input.after) {
|
||||
query.after(input.after as number);
|
||||
|
||||
Reference in New Issue
Block a user