add update post graphql api and tests

This commit is contained in:
Hongbo Wu
2024-06-18 16:17:53 +08:00
parent 9caad12f09
commit 16f67c854c
6 changed files with 178 additions and 8 deletions

View File

@ -20,10 +20,10 @@ export class Post {
user!: User
@Column('uuid', { array: true, nullable: true })
libraryItemIds?: string[]
libraryItemIds?: string[] | null
@Column('uuid', { array: true, nullable: true })
highlightIds?: string[]
highlightIds?: string[] | null
@Column('text')
title!: string
@ -32,10 +32,10 @@ export class Post {
content!: string
@Column('text', { nullable: true })
thumbnail?: string
thumbnail?: string | null
@Column('text', { nullable: true })
thought?: string
thought?: string | null
@Column('timestamptz')
createdAt!: Date

View File

@ -151,7 +151,12 @@ import {
webhookResolver,
webhooksResolver,
} from './index'
import { createPostResolver, postResolver, postsResolver } from './posts'
import {
createPostResolver,
postResolver,
postsResolver,
updatePostResolver,
} from './posts'
import {
markEmailAsItemResolver,
recentEmailsResolver,
@ -319,6 +324,7 @@ export const functionResolvers = {
updateFolderPolicy: updateFolderPolicyResolver,
deleteFolderPolicy: deleteFolderPolicyResolver,
createPost: createPostResolver,
updatePost: updatePostResolver,
},
Query: {
me: getMeUserResolver,
@ -906,4 +912,5 @@ export const functionResolvers = {
...resultResolveTypeResolver('Posts'),
...resultResolveTypeResolver('Post'),
...resultResolveTypeResolver('CreatePost'),
...resultResolveTypeResolver('UpdatePost'),
}

View File

@ -4,6 +4,7 @@ import {
CreatePostErrorCode,
CreatePostSuccess,
MutationCreatePostArgs,
MutationUpdatePostArgs,
PostEdge,
PostErrorCode,
PostResult,
@ -12,12 +13,15 @@ import {
QueryPostArgs,
QueryPostsArgs,
ResolverFn,
UpdatePostError,
UpdatePostErrorCode,
UpdatePostSuccess,
} from '../../generated/graphql'
import {
createPosts,
createPublicPost,
findPublicPostById,
findPublicPostsByUserId,
updatePost,
} from '../../services/post'
import { Merge } from '../../util'
import { authorized } from '../../utils/gql-utils'
@ -135,3 +139,57 @@ export const createPostResolver = authorized<
post,
}
})
export const updatePostResolver = authorized<
Merge<UpdatePostSuccess, { post?: Post }>,
UpdatePostError,
MutationUpdatePostArgs
>(async (_, { input }, { uid, log }) => {
const {
id,
title,
content,
highlightIds,
libraryItemIds,
thought,
thumbnail,
} = input
if (!id || title === null || content === null) {
log.error('Invalid args', { id })
return {
errorCodes: [UpdatePostErrorCode.BadRequest],
}
}
const result = await updatePost(uid, id, {
title,
content,
highlightIds,
libraryItemIds,
thought,
thumbnail,
})
if (!result.affected) {
log.error('Failed to update post', { id })
return {
errorCodes: [UpdatePostErrorCode.Unauthorized],
}
}
const post = await findPublicPostById(id)
if (!post) {
log.error('Post not found', { id })
return {
errorCodes: [UpdatePostErrorCode.Unauthorized],
}
}
return {
post,
}
})

View File

@ -3378,8 +3378,8 @@ const schema = gql`
input UpdatePostInput {
id: ID!
title: String
content: String
title: String @sanitize(minLength: 1, maxLength: 255)
content: String @sanitize(minLength: 1)
thumbnail: String
libraryItemIds: [ID!]
highlightIds: [ID!]

View File

@ -81,3 +81,13 @@ export const findPublicPostById = async (id: string) => {
},
})
}
export const updatePost = async (
userId: string,
postId: string,
post: Partial<Post>
) => {
return authTrx(async (trx) => trx.getRepository(Post).update(postId, post), {
uid: userId,
})
}

View File

@ -288,4 +288,99 @@ describe('Post Resolvers', () => {
await deletePosts(loginUser.id, [postId])
})
})
describe('updatePostResolver', () => {
const mutation = `
mutation UpdatePost($input: UpdatePostInput!) {
updatePost(input: $input) {
... on UpdatePostSuccess {
post {
id
title
content
}
}
... on UpdatePostError {
errorCodes
}
}
}
`
let postId: string
before(async () => {
const post = {
title: 'Post',
content: 'Content',
user: loginUser,
}
const newPost = await createPosts(loginUser.id, [post])
postId = newPost[0].id
})
after(async () => {
await deletePosts(loginUser.id, [postId])
})
it('should return an error if the args are invalid', async () => {
const response = await graphqlRequest(mutation, authToken, {
input: {
id: postId,
title: null,
content: null,
},
})
expect(response.body.data.updatePost.errorCodes).to.eql(['BAD_REQUEST'])
})
it('should return an error if the post is not found', async () => {
const response = await graphqlRequest(mutation, authToken, {
input: {
id: generateFakeUuid(),
title: 'Post',
content: 'Content',
},
})
expect(response.body.data.updatePost.errorCodes).to.eql(['UNAUTHORIZED'])
})
it('should return an error if the user is not the owner of the post', async () => {
const notOwner = await createTestUser('notOwner')
const notOwnerToken = await loginAndGetAuthToken(notOwner.email)
const response = await graphqlRequest(mutation, notOwnerToken, {
input: {
id: postId,
title: 'Post',
content: 'Content',
},
})
expect(response.body.data.updatePost.errorCodes).to.eql(['UNAUTHORIZED'])
await deleteUser(notOwner.id)
})
it('should update the post', async () => {
const response = await graphqlRequest(mutation, authToken, {
input: {
id: postId,
title: 'Updated Post',
content: 'Updated Content',
},
})
expect(response.body.data.updatePost.post.title).to.eql('Updated Post')
expect(response.body.data.updatePost.post.content).to.eql(
'Updated Content'
)
const post = await findPublicPostById(postId)
expect(post?.title).to.eql('Updated Post')
})
})
})