2020-11-19 17:11:23 +03:00
|
|
|
import HttpError from '@wasp/core/HttpError.js'
|
|
|
|
|
2020-11-27 23:47:12 +03:00
|
|
|
import { userPublicSelection } from '../user/queries.js'
|
2020-11-27 18:10:02 +03:00
|
|
|
|
2020-11-27 00:48:35 +03:00
|
|
|
// TODO: I extracted this articleInclude and articleSetFavoritedFields to enable
|
|
|
|
// reusing of logic that shapes articles as they come out of the server,
|
|
|
|
// but I wonder if there is a more elegant way - here there are a lot of assumptions,
|
2020-12-01 20:48:39 +03:00
|
|
|
// and it is easy to not use where it should be used or use it in a wrong setting.
|
2020-11-27 00:48:35 +03:00
|
|
|
|
|
|
|
const articleInclude = {
|
|
|
|
user: {
|
|
|
|
// TODO: Tricky, if you forget this you could return unwanted fields
|
|
|
|
// like hashed password!
|
|
|
|
// It would be cool if we had some protection against making this mistake easily.
|
|
|
|
select: userPublicSelection
|
|
|
|
},
|
2020-11-27 16:25:47 +03:00
|
|
|
tags: true,
|
2020-11-27 00:48:35 +03:00
|
|
|
favoritedBy: {
|
|
|
|
select: {
|
|
|
|
// TODO: Tricky, if I forgot this select here, sensitive data could leak out (hashed password).
|
|
|
|
username: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const articleSetFavoritedFields = (article, user) => {
|
|
|
|
article.favorited = user && article.favoritedBy.find(f => f.username === user.username)
|
|
|
|
article.favoritesCount = article.favoritedBy.length
|
|
|
|
delete article.favoritedBy
|
|
|
|
}
|
|
|
|
|
2020-11-28 00:52:38 +03:00
|
|
|
const getArticles = async (queryArgs, context) => {
|
2020-11-20 20:22:49 +03:00
|
|
|
// TODO: Do some error handling?
|
|
|
|
const articles = await context.entities.Article.findMany({
|
2020-11-28 00:52:38 +03:00
|
|
|
...queryArgs,
|
2020-11-27 00:48:35 +03:00
|
|
|
include: articleInclude
|
2020-11-20 20:22:49 +03:00
|
|
|
})
|
2020-11-27 00:48:35 +03:00
|
|
|
|
|
|
|
for (const article of articles) {
|
|
|
|
articleSetFavoritedFields(article, context.user)
|
|
|
|
}
|
|
|
|
|
2020-11-20 20:22:49 +03:00
|
|
|
return articles
|
|
|
|
}
|
|
|
|
|
2020-11-30 17:00:32 +03:00
|
|
|
export const getArticlesByUser = async ({ username, skip, take }, context) => {
|
|
|
|
const where = { user: { username } }
|
|
|
|
const articles = await getArticles({ where, skip, take }, context)
|
|
|
|
const count = await context.entities.Article.count({ where })
|
|
|
|
return { articles, count }
|
2020-11-27 00:48:35 +03:00
|
|
|
}
|
|
|
|
|
2020-11-30 17:00:32 +03:00
|
|
|
export const getFavoritedArticles = async ({ username, skip, take }, context) => {
|
2020-11-27 00:48:35 +03:00
|
|
|
if (!context.user) { throw new HttpError(403) }
|
2020-11-30 17:00:32 +03:00
|
|
|
const where = { favoritedBy: { some: { username: context.user.username } } }
|
|
|
|
const articles = await getArticles({ where, skip, take }, context)
|
|
|
|
const count = await context.entities.Article.count({ where })
|
|
|
|
return { articles, count }
|
2020-11-27 00:48:35 +03:00
|
|
|
}
|
|
|
|
|
2020-11-30 17:00:32 +03:00
|
|
|
export const getFollowedArticles = async ({ skip, take }, context) => {
|
2020-11-27 20:49:54 +03:00
|
|
|
if (!context.user) { throw new HttpError(403) }
|
|
|
|
|
|
|
|
const followedUsersIds = (await context.entities.User.findUnique({
|
|
|
|
where: { id: context.user.id },
|
|
|
|
include: { following: { select: { id: true } } }
|
|
|
|
})).following.map(({ id }) => id)
|
|
|
|
|
2020-11-30 17:00:32 +03:00
|
|
|
const where = { user: { id: { in: followedUsersIds } } }
|
|
|
|
const articles = await getArticles({ where, skip, take }, context)
|
|
|
|
const count = await context.entities.Article.count({ where })
|
|
|
|
return { articles, count }
|
2020-11-27 20:49:54 +03:00
|
|
|
}
|
|
|
|
|
2020-11-28 00:52:38 +03:00
|
|
|
export const getAllArticles = async ({ skip, take }, context) => {
|
|
|
|
const articles = await getArticles({ skip, take }, context)
|
|
|
|
const count = await context.entities.Article.count()
|
|
|
|
return { articles, count }
|
2020-11-27 20:49:54 +03:00
|
|
|
}
|
|
|
|
|
2020-11-24 23:10:26 +03:00
|
|
|
export const getArticle = async ({ slug }, context) => {
|
2020-11-20 20:22:49 +03:00
|
|
|
// TODO: Do some error handling?
|
2020-11-27 00:48:35 +03:00
|
|
|
const article = await context.entities.Article.findUnique({
|
|
|
|
where: { slug },
|
|
|
|
include: articleInclude
|
|
|
|
})
|
|
|
|
articleSetFavoritedFields(article, context.user)
|
2020-11-20 20:22:49 +03:00
|
|
|
return article
|
|
|
|
}
|
2020-11-25 00:57:14 +03:00
|
|
|
|
|
|
|
export const getArticleComments = async ({ slug }, context) => {
|
|
|
|
// TODO: Do some error handling?
|
|
|
|
const comments = await context.entities.Comment.findMany({
|
|
|
|
where: { article: { slug } },
|
|
|
|
include: {
|
|
|
|
user: {
|
|
|
|
// TODO: Tricky, if you forget this you could return unwanted fields
|
|
|
|
// like hashed password!
|
|
|
|
// It would be cool if we had some protection against making this mistake easily.
|
|
|
|
select: userPublicSelection
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return comments
|
|
|
|
}
|
2020-11-27 23:47:12 +03:00
|
|
|
|
|
|
|
export const getTags = async (_args, context) => {
|
|
|
|
const tags = await context.entities.ArticleTag.findMany()
|
2020-11-30 17:00:32 +03:00
|
|
|
// NOTE: This is expensive!
|
|
|
|
// Consider using a time-limited cache or do some other trick to make it less expensive.
|
2020-11-27 23:47:12 +03:00
|
|
|
for (const tag of tags) {
|
|
|
|
tag.numArticles = await context.entities.Article.count({ where: { tags: { some: { name: tag.name }}}})
|
|
|
|
}
|
|
|
|
return tags
|
|
|
|
}
|