From 1c738b6b025db2e4e46bfa2c786d87717abe32f9 Mon Sep 17 00:00:00 2001 From: Chegele Date: Tue, 25 Jul 2023 19:15:41 -0400 Subject: [PATCH] GraphQL schema and resolvers for rep service --- package-lock.json | 72 +++++++++++- package.json | 3 +- src/app.module.ts | 13 ++- src/graphql.ts | 72 ++++++++++++ .../components/citation/citation.resolver.ts | 64 +++++++++++ .../citation/citation.schema.graphql | 27 +++++ .../components/member/member.resolver.ts | 48 ++++++++ .../components/member/member.schema.graphql | 36 ++++++ .../components/post/post.resolver.ts | 106 ++++++++++++++++++ .../components/post/post.schema.graphql | 26 +++++ src/services/reputation/rep.service.ts | 4 + 11 files changed, 463 insertions(+), 8 deletions(-) create mode 100644 src/graphql.ts create mode 100644 src/services/reputation/components/citation/citation.resolver.ts create mode 100644 src/services/reputation/components/citation/citation.schema.graphql create mode 100644 src/services/reputation/components/member/member.resolver.ts create mode 100644 src/services/reputation/components/member/member.schema.graphql create mode 100644 src/services/reputation/components/post/post.resolver.ts create mode 100644 src/services/reputation/components/post/post.schema.graphql create mode 100644 src/services/reputation/rep.service.ts diff --git a/package-lock.json b/package-lock.json index 71fc4f3..eec12d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "@nestjs/platform-express": "^10.0.0", "graphql": "^16.6.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "ts-morph": "^19.0.0" }, "devDependencies": { "@nestjs/cli": "^10.0.0", @@ -2097,6 +2098,53 @@ "@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": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -3061,8 +3109,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -3492,6 +3539,11 @@ "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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -6596,6 +6648,11 @@ "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": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8010,6 +8067,15 @@ "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": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", diff --git a/package.json b/package.json index bfceac1..b94967c 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "@nestjs/platform-express": "^10.0.0", "graphql": "^16.6.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "ts-morph": "^19.0.0" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index a0b3fb9..5fdd51f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,19 +1,24 @@ -import { Module } from '@nestjs/common'; +import { Module, Post } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; 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({ imports: [ GraphQLModule.forRoot({ driver: ApolloDriver, - autoSchemaFile: join(process.cwd(), 'src/schema.gql'), - sortSchema: true, + typePaths: ['./**/*.graphql'], + definitions: { + path: join(process.cwd(), 'src/graphql.ts') + }, }), ], controllers: [AppController], - providers: [AppService], + providers: [AppService, MemberResolver, PostResolver, CitationsResolver], }) export class AppModule {} diff --git a/src/graphql.ts b/src/graphql.ts new file mode 100644 index 0000000..c766b93 --- /dev/null +++ b/src/graphql.ts @@ -0,0 +1,72 @@ + +/* + * ------------------------------------------------------- + * THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) + * ------------------------------------------------------- + */ + +/* tslint:disable */ +/* eslint-disable */ + +export interface IQuery { + citation(id: string): Nullable | Promise>; + citations(): Nullable[] | Promise[]>; + isNeutral(citationId: string): Nullable | Promise>; + isPositive(citationId: string): Nullable | Promise>; + isNegative(citationId: string): Nullable | Promise>; + adjacentPost(citationId: string, postId: string): Nullable | Promise>; + members(): Nullable[]> | Promise[]>>; + member(id: string): Nullable | Promise>; + posts(): Nullable[] | Promise[]>; + post(id: string): Nullable | Promise>; + authorWeight(postId: string, authorId: string): Nullable | Promise>; + citationWeight(postId: string, citationId: string): Nullable | Promise>; +} + +export interface Citation { + id: string; + sourcePost: Post; + citedPost: Post; + directional: boolean; + impact: number; +} + +export interface IMutation { + createCitation(citationId?: Nullable, sourcePostId?: Nullable, citedPostId?: Nullable, impact?: Nullable): Nullable | Promise>; + createMember(id?: Nullable): Nullable | Promise>; + postRep(memberId: string, postId: string, amount: number): Nullable | Promise>; + citationRep(memberId: string, postId: string, citationId: string, amount: number): Nullable | Promise>; + createPost(id?: Nullable, title?: Nullable, content?: Nullable, authorId?: Nullable): Nullable | Promise>; + setAuthor(postId: string, authorId: string, weight: number): Nullable | Promise>; + removeAuthor(postId: string, authorId: string): Nullable | Promise>; + setCitation(postId: string, citationId: string, weight: number): Nullable | Promise>; + removeCitation(postId: string, citationId: string): Nullable | Promise>; +} + +export interface Member { + id: string; + reputation: number; + ledger?: Nullable[]>; +} + +export interface LedgerEntry { + timestamp?: Nullable; + type?: Nullable; + postId?: Nullable; + citationId?: Nullable; + change?: Nullable; + balance?: Nullable; +} + +export interface Post { + id: string; + title: string; + content: string; + authors: Nullable[]; + citations: Nullable[]; + authorsWeightTotal: number; + citationsWeightTotal: number; + data: string; +} + +type Nullable = T | null; diff --git a/src/services/reputation/components/citation/citation.resolver.ts b/src/services/reputation/components/citation/citation.resolver.ts new file mode 100644 index 0000000..e832ef8 --- /dev/null +++ b/src/services/reputation/components/citation/citation.resolver.ts @@ -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); + } +} diff --git a/src/services/reputation/components/citation/citation.schema.graphql b/src/services/reputation/components/citation/citation.schema.graphql new file mode 100644 index 0000000..edc788d --- /dev/null +++ b/src/services/reputation/components/citation/citation.schema.graphql @@ -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 +} + diff --git a/src/services/reputation/components/member/member.resolver.ts b/src/services/reputation/components/member/member.resolver.ts new file mode 100644 index 0000000..a032edb --- /dev/null +++ b/src/services/reputation/components/member/member.resolver.ts @@ -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); + } + + +} \ No newline at end of file diff --git a/src/services/reputation/components/member/member.schema.graphql b/src/services/reputation/components/member/member.schema.graphql new file mode 100644 index 0000000..60a1f4c --- /dev/null +++ b/src/services/reputation/components/member/member.schema.graphql @@ -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 +} \ No newline at end of file diff --git a/src/services/reputation/components/post/post.resolver.ts b/src/services/reputation/components/post/post.resolver.ts new file mode 100644 index 0000000..00018fc --- /dev/null +++ b/src/services/reputation/components/post/post.resolver.ts @@ -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; + } + +} \ No newline at end of file diff --git a/src/services/reputation/components/post/post.schema.graphql b/src/services/reputation/components/post/post.schema.graphql new file mode 100644 index 0000000..22c7445 --- /dev/null +++ b/src/services/reputation/components/post/post.schema.graphql @@ -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 +} diff --git a/src/services/reputation/rep.service.ts b/src/services/reputation/rep.service.ts new file mode 100644 index 0000000..4f33503 --- /dev/null +++ b/src/services/reputation/rep.service.ts @@ -0,0 +1,4 @@ + +export class ReputationService { + +} \ No newline at end of file