GraphQL schema and resolvers for rep service

This commit is contained in:
Chegele 2023-07-25 19:15:41 -04:00
parent 3259cbf001
commit 1c738b6b02
11 changed files with 463 additions and 8 deletions

72
package-lock.json generated
View File

@ -17,7 +17,8 @@
"@nestjs/platform-express": "^10.0.0", "@nestjs/platform-express": "^10.0.0",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1" "rxjs": "^7.8.1",
"ts-morph": "^19.0.0"
}, },
"devDependencies": { "devDependencies": {
"@nestjs/cli": "^10.0.0", "@nestjs/cli": "^10.0.0",
@ -2097,6 +2098,53 @@
"@sinonjs/commons": "^3.0.0" "@sinonjs/commons": "^3.0.0"
} }
}, },
"node_modules/@ts-morph/common": {
"version": "0.20.0",
"resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.20.0.tgz",
"integrity": "sha512-7uKjByfbPpwuzkstL3L5MQyuXPSKdoNG93Fmi2JoDcTf3pEP731JdRFAduRVkOs8oqxPsXKA+ScrWkdQ8t/I+Q==",
"dependencies": {
"fast-glob": "^3.2.12",
"minimatch": "^7.4.3",
"mkdirp": "^2.1.6",
"path-browserify": "^1.0.1"
}
},
"node_modules/@ts-morph/common/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@ts-morph/common/node_modules/minimatch": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz",
"integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@ts-morph/common/node_modules/mkdirp": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -3061,8 +3109,7 @@
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
"dev": true
}, },
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
@ -3492,6 +3539,11 @@
"node": ">= 0.12.0" "node": ">= 0.12.0"
} }
}, },
"node_modules/code-block-writer": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w=="
},
"node_modules/collect-v8-coverage": { "node_modules/collect-v8-coverage": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
@ -6596,6 +6648,11 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
},
"node_modules/path-exists": { "node_modules/path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -8010,6 +8067,15 @@
"webpack": "^5.0.0" "webpack": "^5.0.0"
} }
}, },
"node_modules/ts-morph": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-19.0.0.tgz",
"integrity": "sha512-D6qcpiJdn46tUqV45vr5UGM2dnIEuTGNxVhg0sk5NX11orcouwj6i1bMqZIz2mZTZB1Hcgy7C3oEVhAT+f6mbQ==",
"dependencies": {
"@ts-morph/common": "~0.20.0",
"code-block-writer": "^12.0.0"
}
},
"node_modules/ts-node": { "node_modules/ts-node": {
"version": "10.9.1", "version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",

View File

@ -28,7 +28,8 @@
"@nestjs/platform-express": "^10.0.0", "@nestjs/platform-express": "^10.0.0",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1" "rxjs": "^7.8.1",
"ts-morph": "^19.0.0"
}, },
"devDependencies": { "devDependencies": {
"@nestjs/cli": "^10.0.0", "@nestjs/cli": "^10.0.0",

View File

@ -1,19 +1,24 @@
import { Module } from '@nestjs/common'; import { Module, Post } from '@nestjs/common';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql'; import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path'; import { join } from 'path';
import { MemberResolver } from './services/reputation/components/member/member.resolver';
import { PostResolver } from './services/reputation/components/post/post.resolver';
import { CitationsResolver } from './services/reputation/components/citation/citation.resolver';
@Module({ @Module({
imports: [ imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({ GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver, driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'), typePaths: ['./**/*.graphql'],
sortSchema: true, definitions: {
path: join(process.cwd(), 'src/graphql.ts')
},
}), }),
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService, MemberResolver, PostResolver, CitationsResolver],
}) })
export class AppModule {} export class AppModule {}

72
src/graphql.ts Normal file
View File

@ -0,0 +1,72 @@
/*
* -------------------------------------------------------
* THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
* -------------------------------------------------------
*/
/* tslint:disable */
/* eslint-disable */
export interface IQuery {
citation(id: string): Nullable<Citation> | Promise<Nullable<Citation>>;
citations(): Nullable<Citation>[] | Promise<Nullable<Citation>[]>;
isNeutral(citationId: string): Nullable<boolean> | Promise<Nullable<boolean>>;
isPositive(citationId: string): Nullable<boolean> | Promise<Nullable<boolean>>;
isNegative(citationId: string): Nullable<boolean> | Promise<Nullable<boolean>>;
adjacentPost(citationId: string, postId: string): Nullable<Post> | Promise<Nullable<Post>>;
members(): Nullable<Nullable<Member>[]> | Promise<Nullable<Nullable<Member>[]>>;
member(id: string): Nullable<Member> | Promise<Nullable<Member>>;
posts(): Nullable<Post>[] | Promise<Nullable<Post>[]>;
post(id: string): Nullable<Post> | Promise<Nullable<Post>>;
authorWeight(postId: string, authorId: string): Nullable<number> | Promise<Nullable<number>>;
citationWeight(postId: string, citationId: string): Nullable<number> | Promise<Nullable<number>>;
}
export interface Citation {
id: string;
sourcePost: Post;
citedPost: Post;
directional: boolean;
impact: number;
}
export interface IMutation {
createCitation(citationId?: Nullable<string>, sourcePostId?: Nullable<string>, citedPostId?: Nullable<string>, impact?: Nullable<number>): Nullable<Citation> | Promise<Nullable<Citation>>;
createMember(id?: Nullable<string>): Nullable<Member> | Promise<Nullable<Member>>;
postRep(memberId: string, postId: string, amount: number): Nullable<LedgerEntry> | Promise<Nullable<LedgerEntry>>;
citationRep(memberId: string, postId: string, citationId: string, amount: number): Nullable<LedgerEntry> | Promise<Nullable<LedgerEntry>>;
createPost(id?: Nullable<string>, title?: Nullable<string>, content?: Nullable<string>, authorId?: Nullable<string>): Nullable<Post> | Promise<Nullable<Post>>;
setAuthor(postId: string, authorId: string, weight: number): Nullable<boolean> | Promise<Nullable<boolean>>;
removeAuthor(postId: string, authorId: string): Nullable<boolean> | Promise<Nullable<boolean>>;
setCitation(postId: string, citationId: string, weight: number): Nullable<boolean> | Promise<Nullable<boolean>>;
removeCitation(postId: string, citationId: string): Nullable<boolean> | Promise<Nullable<boolean>>;
}
export interface Member {
id: string;
reputation: number;
ledger?: Nullable<Nullable<LedgerEntry>[]>;
}
export interface LedgerEntry {
timestamp?: Nullable<string>;
type?: Nullable<string>;
postId?: Nullable<string>;
citationId?: Nullable<string>;
change?: Nullable<number>;
balance?: Nullable<number>;
}
export interface Post {
id: string;
title: string;
content: string;
authors: Nullable<Member>[];
citations: Nullable<Citation>[];
authorsWeightTotal: number;
citationsWeightTotal: number;
data: string;
}
type Nullable<T> = T | null;

View File

@ -0,0 +1,64 @@
import { Resolver, Query, Args, Mutation } from '@nestjs/graphql';
import { Citation } from './citation';
import { Post } from '../post/post';
@Resolver(of => Citation)
export class CitationsResolver {
constructor() {}
@Query()
citations() {
return Citation.getAllCitations();
}
@Query()
citation(@Args('id') id: string) {
return Citation.getCitation(id);
}
@Query()
isNeutral(@Args('citationId'!) id: string) {
const citation = Citation.getCitation(id);
if (!citation) return null;
return citation.isNeutral();
}
@Query()
isPositive(@Args('citationId'!) id: string) {
const citation = Citation.getCitation(id);
if (!citation) return null;
return citation.isPositive();
}
@Query()
isNegative(@Args('citationId'!) id: string) {
const citation = Citation.getCitation(id);
if (!citation) return null;
return citation.isNegative();
}
@Query()
adjacentPost(
@Args('citationId'!) citationId: string,
@Args('postId'!) postId: string
) {
const citation = Citation.getCitation(citationId);
const post = Post.getPost(postId);
if (!citation || !post) return null;
return citation.getAdjacent(post);
}
@Mutation()
createCitation(
@Args('citationId') id: string,
@Args('sourcePostId'!) sourcePostId: string,
@Args('citedPostId'!) citedPostId: string,
@Args('impact'!) impact: number
) {
const sourcePost = Post.getPost(sourcePostId);
const citedPost = Post.getPost(citedPostId);
if (!sourcePost || !citedPost) return null;
return new Citation(id, sourcePost, citedPost, impact);
}
}

View File

@ -0,0 +1,27 @@
type Query {
citation(id: String!): Citation
citations: [Citation]!
isNeutral(citationId: String!): Boolean
isPositive(citationId: String!): Boolean
isNegative(citationId: String!): Boolean
adjacentPost(citationId: String!, postId: String!): Post
}
type Citation {
id: String!
sourcePost: Post!
citedPost: Post!
directional: Boolean!
impact: Int!
}
type Mutation {
createCitation(
citationId: String,
sourcePostId: String,
citedPostId: String,
impact: Int
): Citation
}

View File

@ -0,0 +1,48 @@
import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
import { Member } from "./member";
@Resolver(of => Member)
export class MemberResolver {
constructor() {}
@Query()
members() {
return Member.getAllMembers();
}
@Query()
member(@Args('id') id: string) {
return Member.getMember(id);
}
@Mutation()
createMember(@Args('id') id?: string) {
return new Member(id);
}
@Mutation()
postRep(
@Args('memberId'!) memberId: string,
@Args('postId'!) postId: string,
@Args('amount'!) amount: number,
) {
const member = Member.getMember(memberId);
if (!member) return null;
return member.postReputation(postId, amount);
}
@Mutation()
citationRep(
@Args('memberId'!) memberId: string,
@Args('postId'!) postId: string,
@Args('citationId'!) citationId: string,
@Args('amount'!) amount: number,
) {
const member = Member.getMember(memberId);
if (!member) return null;
return member.citationReputation(postId, citationId, amount);
}
}

View File

@ -0,0 +1,36 @@
type Query {
members: [Member]
member(id: String!): Member
}
type Member {
id: String!
reputation: Int!
ledger: [LedgerEntry]
}
type LedgerEntry {
timestamp: String
type: String
postId: String
citationId: String
change: Int
balance: Int
}
type Mutation {
createMember(id: String): Member
postRep(
memberId: String!,
postId: String!,
amount: Int!
): LedgerEntry
citationRep(
memberId: String!,
postId: String!,
citationId: String!,
amount: Int!
): LedgerEntry
}

View File

@ -0,0 +1,106 @@
import { Resolver, Query, Args, Mutation } from "@nestjs/graphql";
import { Post } from "./post";
import { Member } from "../member/member";
import { Citation } from "../citation/citation";
@Resolver(of => Post)
export class PostResolver {
constructor() {}
@Query()
posts() {
return Post.getAllPosts();
}
@Query()
post(@Args('id') id: string) {
return Post.getPost(id);
}
@Query()
authorWeight(
@Args('postId'!) postId: string,
@Args('authorId'!) authorId: string
) {
const post = Post.getPost(postId);
const author = Member.getMember(authorId);
if (!post || !author) return null;
return post.getWeight(author);
}
@Query()
citationWeight(
@Args('postId'!) postId: string,
@Args('citationId'!) citationId: string
) {
const post = Post.getPost(postId);
const citation = Citation.getCitation(citationId);
if (!post || !citation) return null;
return post.getWeight(citation);
}
@Mutation()
createPost(
@Args('id') id: string,
@Args('title'!) title: string,
@Args('content'!) content: string,
@Args('authorId'!) authorId: string,
) {
const author = Member.getMember(authorId);
if (!author) return null;
return new Post(id, title, content, author);
}
@Mutation()
setAuthor(
@Args('postId'!) postId: string,
@Args('authorId'!) authorId: string,
@Args('weight'!) weight: number,
) {
const post = Post.getPost(postId);
const author = Member.getMember(authorId);
if (!post || !author) return false;
post.setAuthor(author, weight);
return true;
}
@Mutation()
removeAuthor(
@Args('postId'!) postId: string,
@Args('citationId'!) authorId: string,
) {
const post = Post.getPost(postId);
const author = Member.getMember(authorId);
if (!post || !author) return false;
post.removeAuthor(author);
return true;
}
@Mutation()
setCitation(
@Args('postId'!) postId: string,
@Args('citationId'!) citationId: string,
@Args('weight'!) weight: number,
) {
const post = Post.getPost(postId);
const citation = Citation.getCitation(citationId);
if (!post || !citation) return false;
post.setCitation(citation, weight);
return true;
}
@Mutation()
removeCitation(
@Args('postId'!) postId: string,
@Args('citationId'!) citationId: string,
) {
const post = Post.getPost(postId);
const citation = Citation.getCitation(citationId);
if (!post || !citation) return false;
post.removeCitation(citation);
return true;
}
}

View File

@ -0,0 +1,26 @@
type Query {
posts: [Post]!
post(id: String!): Post
authorWeight(postId: String!, authorId: String!): Int
citationWeight(postId: String!, citationId: String!): Int
}
type Post {
id: ID!
title: String!
content: String!
authors: [Member]!
citations: [Citation]!
authorsWeightTotal: Int!
citationsWeightTotal: Int!
data: String!
}
type Mutation {
createPost(id: String, title: String, content: String, authorId: String): Post
setAuthor(postId: String!, authorId: String!, weight: Int!): Boolean
removeAuthor(postId: String!, authorId: String!): Boolean
setCitation(postId: String!, citationId: String!, weight: Int!): Boolean
removeCitation(postId: String!, citationId: String!): Boolean
}

View File

@ -0,0 +1,4 @@
export class ReputationService {
}