From d0e94808bb837845444abbe726ffd200c9977201 Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Tue, 22 Jan 2019 13:06:01 +0100 Subject: [PATCH] Fix CommentRoutes --- packages/common/src/IAPIDef.ts | 8 ++-- packages/common/src/index.ts | 2 +- packages/server/src/routes/CommentRoutes.ts | 28 ++++++----- .../server/src/services/CommentService.ts | 10 ++-- .../server/src/services/ICommentService.ts | 2 +- packages/server/src/services/ISiteService.ts | 2 + packages/server/src/services/SiteService.ts | 11 ++++- packages/server/src/services/StoryService.ts | 48 +++++++++++++++++-- packages/server/src/services/TeamService.ts | 1 + 9 files changed, 81 insertions(+), 31 deletions(-) diff --git a/packages/common/src/IAPIDef.ts b/packages/common/src/IAPIDef.ts index e31b74a..ae526fb 100644 --- a/packages/common/src/IAPIDef.ts +++ b/packages/common/src/IAPIDef.ts @@ -1,5 +1,6 @@ -import {ICredentials} from './ICredentials' +import {ICommentTree} from './ICommentTree' import {IComment} from './IComment' +import {ICredentials} from './ICredentials' import {IUser} from './IUser' export interface IAPIDef { @@ -34,13 +35,14 @@ export interface IAPIDef { } '/story/:storyId/comments': { 'get': { - response: IComment[], + response: ICommentTree, params: { - storyId: number // TODO might have to change to string (url) + storyId: number } } 'post': { response: IComment, + body: IComment, params: { storyId: number } diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index d4f820e..33594de 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -4,5 +4,5 @@ export * from './ICommentTree' export * from './ICredentials' export * from './IRoutes' export * from './ISite' +export * from './ITeam' export * from './IUser' -import * from './ITeam' diff --git a/packages/server/src/routes/CommentRoutes.ts b/packages/server/src/routes/CommentRoutes.ts index 4a790e4..33e9b7b 100644 --- a/packages/server/src/routes/CommentRoutes.ts +++ b/packages/server/src/routes/CommentRoutes.ts @@ -1,6 +1,7 @@ import {AsyncRouter} from '../router' import {BaseRoute} from './BaseRoute' import {IAPIDef} from '@rondo/common' +import {ICommentService} from '../services/ICommentService' import {ensureLoggedInApi} from '../middleware' export class CommentRoutes extends BaseRoute { @@ -15,53 +16,54 @@ export class CommentRoutes extends BaseRoute { t.get('/story/:storyId/comments', async req => { const {storyId} = req.params - // TODO retrieve comments from story - return [] + return this.commentService.find(storyId) }) t.use(ensureLoggedInApi) t.post('/story/:storyId/comments', async req => { + const {storyId} = req.params const comment = req.body - // TODO save a comment - return comment + comment.storyId = storyId + return this.commentService.saveRoot(comment, req.user!.id) }) t.post('/comments/:parentId', async req => { + const {parentId} = req.params const comment = req.body - // TODO save a comment - return comment + comment.parentId = parentId + return this.commentService.save(comment, req.user!.id) }) t.put('/comments/:commentId', async req => { const comment = req.body - // TODO edit a comment - return comment + comment.id = req.params.commentId + return this.commentService.edit(comment, req.user!.id) }) t.delete('/comments/:commentId', async req => { const {commentId} = req.params - // TODO delete a comment + return this.commentService.delete(commentId, req.user!.id) }) t.post('/comments/:commentId/vote', async req => { const {commentId} = req.params - // TODO upvote a comment + return this.commentService.upVote(commentId, req.user!.id) }) t.delete('/comments/:commentId/vote', async req => { const {commentId} = req.params - // TODO delete a vote + return this.commentService.deleteVote(commentId, req.user!.id) }) t.post('/comments/:commentId/spam', async req => { const {commentId} = req.params - // TODO report comment as spam + return this.commentService.markAsSpam(commentId, req.user!.id) }) t.delete('/comments/:commentId/spam', async req => { const {commentId} = req.params - // TODO delete spam report + return this.commentService.unmarkAsSpam(commentId, req.user!.id) }) } diff --git a/packages/server/src/services/CommentService.ts b/packages/server/src/services/CommentService.ts index 5c54948..72b43bb 100644 --- a/packages/server/src/services/CommentService.ts +++ b/packages/server/src/services/CommentService.ts @@ -89,20 +89,16 @@ export class CommentService extends BaseService implements ICommentService { return editedComment } - async delete(comment: IComment, userId: number) { - new Validator(comment) - .ensure('id') - .throw() - + async delete(commentId: number, userId: number) { await this.getRepository(Comment) .update({ - id: comment.id, + id: commentId, userId, }, { message: '(this message has been removed by the user)', }) - return this.findOne(comment.id) + return this.findOne(commentId) } async upVote(commentId: number, userId: number) { diff --git a/packages/server/src/services/ICommentService.ts b/packages/server/src/services/ICommentService.ts index e86f737..f962411 100644 --- a/packages/server/src/services/ICommentService.ts +++ b/packages/server/src/services/ICommentService.ts @@ -11,7 +11,7 @@ export interface ICommentService { edit(comment: IComment, userId: number): Promise - delete(comment: IComment, userId: number): Promise + delete(commentId: number, userId: number): Promise upVote(commentId: number, userId: number): Promise diff --git a/packages/server/src/services/ISiteService.ts b/packages/server/src/services/ISiteService.ts index 6af62e9..fe05942 100644 --- a/packages/server/src/services/ISiteService.ts +++ b/packages/server/src/services/ISiteService.ts @@ -7,5 +7,7 @@ export interface ISiteService { find(userId: number): Promise + findByDomain(domain: string): Promise + // TODO add other methods } diff --git a/packages/server/src/services/SiteService.ts b/packages/server/src/services/SiteService.ts index 3d20fab..84fcdfa 100644 --- a/packages/server/src/services/SiteService.ts +++ b/packages/server/src/services/SiteService.ts @@ -4,6 +4,7 @@ import {Site} from '../entities/Site' export class SiteService extends BaseService implements ISiteService { async create(name: string, teamId: number, userId: number) { + // TODO check site limit per user return this.getRepository(Site).save({ name, teamId, @@ -11,7 +12,7 @@ export class SiteService extends BaseService implements ISiteService { }) } - findOne(id: number, teamId: number) { + async findOne(id: number, teamId: number) { return this.getRepository(Site).findOne({ where: { id, @@ -20,6 +21,14 @@ export class SiteService extends BaseService implements ISiteService { }) } + async findByDomain(domain: string) { + return this.getRepository(Site).findOne({ + where: { + domain, + }, + }) + } + async find(teamId: number) { return this.getRepository(Site).find({ where: { teamId }, diff --git a/packages/server/src/services/StoryService.ts b/packages/server/src/services/StoryService.ts index 35ab8ee..18409b6 100644 --- a/packages/server/src/services/StoryService.ts +++ b/packages/server/src/services/StoryService.ts @@ -1,10 +1,48 @@ -import {IStoryService} from './IStoryService' -import {Story} from '../entities/Story' import URL from 'url' +import {BaseService} from './BaseService' +import {ISiteService} from './ISiteService' +import {IStoryService} from './IStoryService' +import {ITransactionManager} from '../database/ITransactionManager' +import {Story} from '../entities/Story' + +export class StoryService extends BaseService implements IStoryService { + constructor( + transactionManager: ITransactionManager, + protected readonly siteService: ISiteService, + ) { + super(transactionManager) + } -export class StoryService implements IStoryService { async findOne(url: string) { - const parsedUrl = URL.parse(url) - const hostname = parsedUrl.hostname + const storyRepo = this.getRepository(Story) + const story = await storyRepo.findOne({ + where: {url}, + }) + if (story) { + return story + } + + const domain = URL.parse(url).hostname + const site = await this.siteService.findByDomain(domain!) + + if (!site) { + return undefined + } + + try { + return await storyRepo.save({ + url, + siteId: site.id, + }) + } catch (err) { + // TODO check if unique constrint error + + // This could happen if there are two concurrent requests coming in at + // the same time, and they both cannot find the story, then decide to + // create a record. + return await storyRepo.findOne({ + where: {url}, + }) + } } } diff --git a/packages/server/src/services/TeamService.ts b/packages/server/src/services/TeamService.ts index f217bb1..fb33243 100644 --- a/packages/server/src/services/TeamService.ts +++ b/packages/server/src/services/TeamService.ts @@ -3,6 +3,7 @@ import {ITeamService} from './ITeamService' import {Team} from '../entities/Team' export class TeamService extends BaseService implements ITeamService { + // TODO check team limit per user async create(name: string, userId: number) { return this.getRepository(Team).save({ name,