add create post graphql api and tests

This commit is contained in:
Hongbo Wu 2024-06-18 15:55:06 +08:00
parent 12db8b6a7d
commit 9caad12f09
8 changed files with 117 additions and 9 deletions

View file

@ -515,7 +515,6 @@ export type CreatePostError = {
};
export enum CreatePostErrorCode {
BadRequest = 'BAD_REQUEST',
Unauthorized = 'UNAUTHORIZED'
}

View file

@ -460,7 +460,6 @@ type CreatePostError {
}
enum CreatePostErrorCode {
BAD_REQUEST
UNAUTHORIZED
}

View file

@ -72,7 +72,7 @@ export const authTrx = async <T>(
): Promise<T> => {
let { uid, userRole } = options
// if uid and dbRole are not passed in, then get them from the claims
// if uid and dbRole are not passed in, then get them from the http context
if (!uid && !userRole) {
const claims: Claims | undefined = httpContext.get('claims')
uid = claims?.uid

View file

@ -151,7 +151,7 @@ import {
webhookResolver,
webhooksResolver,
} from './index'
import { postResolver, postsResolver } from './posts'
import { createPostResolver, postResolver, postsResolver } from './posts'
import {
markEmailAsItemResolver,
recentEmailsResolver,
@ -318,6 +318,7 @@ export const functionResolvers = {
createFolderPolicy: createFolderPolicyResolver,
updateFolderPolicy: updateFolderPolicyResolver,
deleteFolderPolicy: deleteFolderPolicyResolver,
createPost: createPostResolver,
},
Query: {
me: getMeUserResolver,
@ -904,4 +905,5 @@ export const functionResolvers = {
...resultResolveTypeResolver('DeleteFolderPolicy'),
...resultResolveTypeResolver('Posts'),
...resultResolveTypeResolver('Post'),
...resultResolveTypeResolver('CreatePost'),
}

View file

@ -1,5 +1,9 @@
import { Post } from '../../entity/post'
import {
CreatePostError,
CreatePostErrorCode,
CreatePostSuccess,
MutationCreatePostArgs,
PostEdge,
PostErrorCode,
PostResult,
@ -10,10 +14,13 @@ import {
ResolverFn,
} from '../../generated/graphql'
import {
createPosts,
createPublicPost,
findPublicPostById,
findPublicPostsByUserId,
} from '../../services/post'
import { Merge } from '../../util'
import { authorized } from '../../utils/gql-utils'
import { ResolverContext } from '../types'
type PartialPostEdge = Merge<
@ -97,3 +104,34 @@ export const postResolver: ResolverFn<
}
}
export const createPostResolver = authorized<
Merge<CreatePostSuccess, { post: Post }>,
CreatePostError,
MutationCreatePostArgs
>(async (_, { input }, { uid, log }) => {
const { title, content, highlightIds, libraryItemIds, thought, thumbnail } =
input
const postToCreate = {
userId: uid,
title,
content,
highlightIds: highlightIds || undefined,
libraryItemIds: libraryItemIds || undefined,
thought: thought || undefined,
thumbnail: thumbnail || undefined,
}
const post = await createPublicPost(uid, postToCreate)
if (!post) {
log.error('Failed to create post', { postToCreate })
return {
errorCodes: [CreatePostErrorCode.Unauthorized],
}
}
return {
post,
}
})

View file

@ -3354,8 +3354,8 @@ const schema = gql`
}
input CreatePostInput {
title: String!
content: String!
title: String! @sanitize(minLength: 1, maxLength: 255)
content: String! @sanitize(minLength: 1)
thumbnail: String
libraryItemIds: [ID!]
highlightIds: [ID!]
@ -3374,7 +3374,6 @@ const schema = gql`
enum CreatePostErrorCode {
UNAUTHORIZED
BAD_REQUEST
}
input UpdatePostInput {

View file

@ -1,5 +1,6 @@
import { DeepPartial } from 'typeorm'
import { Post } from '../entity/post'
import { Profile } from '../entity/profile'
import { authTrx, getRepository } from '../repository'
export const findPublicPostsByUserId = async (
@ -26,6 +27,30 @@ export const findPublicPostsByUserId = async (
return posts
}
export const createPublicPost = async (
userId: string,
post: DeepPartial<Post>
) => {
return authTrx(
async (trx) => {
const newPost = await trx.getRepository(Post).save(post)
// Make user profile public when user creates a post
await trx.getRepository(Profile).update(
{ user: { id: userId } },
{
private: false,
}
)
return newPost
},
{
uid: userId,
}
)
}
export const createPosts = async (
userId: string,
posts: Array<DeepPartial<Post>>

View file

@ -1,7 +1,11 @@
import { expect } from 'chai'
import { User } from '../../src/entity/user'
import { createPosts, deletePosts } from '../../src/services/post'
import { updateProfile } from '../../src/services/profile'
import {
createPosts,
deletePosts,
findPublicPostById,
} from '../../src/services/post'
import { findProfile, updateProfile } from '../../src/services/profile'
import { deleteUser } from '../../src/services/user'
import { createTestUser } from '../db'
import { generateFakeUuid, graphqlRequest, loginAndGetAuthToken } from '../util'
@ -242,4 +246,46 @@ describe('Post Resolvers', () => {
})
})
})
describe('createPostResolver', () => {
const mutation = `
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
... on CreatePostSuccess {
post {
id
title
content
}
}
... on CreatePostError {
errorCodes
}
}
}
`
it('should create a post', async () => {
const response = await graphqlRequest(mutation, authToken, {
input: {
title: 'Post',
content: 'Content',
},
})
expect(response.body.data.createPost.post.title).to.eql('Post')
expect(response.body.data.createPost.post.content).to.eql('Content')
const postId = response.body.data.createPost.post.id as string
const post = await findPublicPostById(postId)
expect(post).to.exist
expect(post?.title).to.eql('Post')
const profile = await findProfile(loginUser)
expect(profile?.private).to.be.false
await deletePosts(loginUser.id, [postId])
})
})
})