-
-
Notifications
You must be signed in to change notification settings - Fork 894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Community post tags (part 2: API methods) #5389
base: main
Are you sure you want to change the base?
Changes from 5 commits
d69ccc8
ee045ad
1f4c451
55e71b9
c13feef
5335294
475f2fd
f920ca5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
jest.setTimeout(120000); | ||
|
||
import { | ||
alpha, | ||
beta, | ||
setupLogins, | ||
createCommunity, | ||
unfollows, | ||
randomString, | ||
createPost, | ||
} from "./shared"; | ||
import { CreateCommunityTag } from "lemmy-js-client/dist/types/CreateCommunityTag"; | ||
import { UpdateCommunityTag } from "lemmy-js-client/dist/types/UpdateCommunityTag"; | ||
import { DeleteCommunityTag } from "lemmy-js-client/dist/types/DeleteCommunityTag"; | ||
import { ListCommunityTags } from "lemmy-js-client/dist/types/ListCommunityTags"; | ||
import { UpdatePostTags } from "lemmy-js-client/dist/types/UpdatePostTags"; | ||
|
||
beforeAll(setupLogins); | ||
afterAll(unfollows); | ||
|
||
test("Create, update, delete community tag", async () => { | ||
// Create a community first | ||
let communityRes = await createCommunity(alpha); | ||
const communityId = communityRes.community_view.community.id; | ||
|
||
// Create a tag | ||
const tagName = randomString(10); | ||
const tagSlug = tagName.toLowerCase(); | ||
let createForm: CreateCommunityTag = { | ||
name: tagName, | ||
id_slug: tagSlug, | ||
community_id: communityId, | ||
}; | ||
let createRes = await alpha.createCommunityTag(createForm); | ||
expect(createRes.id).toBeDefined(); | ||
expect(createRes.name).toBe(tagName); | ||
expect(createRes.community_id).toBe(communityId); | ||
|
||
// Update the tag | ||
const newTagName = randomString(10); | ||
let updateForm: UpdateCommunityTag = { | ||
tag_id: createRes.id, | ||
name: newTagName, | ||
}; | ||
let updateRes = await alpha.updateCommunityTag(updateForm); | ||
expect(updateRes.id).toBe(createRes.id); | ||
expect(updateRes.name).toBe(newTagName); | ||
expect(updateRes.community_id).toBe(communityId); | ||
|
||
// List tags | ||
let listForm: ListCommunityTags = { | ||
community_id: communityId, | ||
}; | ||
let listRes = await alpha.listCommunityTags(listForm); | ||
expect(listRes.tags.length).toBeGreaterThan(0); | ||
expect(listRes.tags.find(t => t.id === createRes.id)?.name).toBe(newTagName); | ||
|
||
// Delete the tag | ||
let deleteForm: DeleteCommunityTag = { | ||
tag_id: createRes.id, | ||
}; | ||
let deleteRes = await alpha.deleteCommunityTag(deleteForm); | ||
expect(deleteRes.id).toBe(createRes.id); | ||
|
||
// Verify tag is deleted | ||
listRes = await alpha.listCommunityTags(listForm); | ||
expect(listRes.tags.find(t => t.id === createRes.id)).toBeUndefined(); | ||
}); | ||
|
||
test("Update post tags", async () => { | ||
// Create a community | ||
let communityRes = await createCommunity(alpha); | ||
const communityId = communityRes.community_view.community.id; | ||
|
||
// Create two tags | ||
const tag1Name = randomString(10); | ||
const tag1Slug = tag1Name.toLowerCase(); | ||
let createForm1: CreateCommunityTag = { | ||
name: tag1Name, | ||
id_slug: tag1Slug, | ||
community_id: communityId, | ||
}; | ||
let tag1Res = await alpha.createCommunityTag(createForm1); | ||
expect(tag1Res.id).toBeDefined(); | ||
|
||
const tag2Name = randomString(10); | ||
const tag2Slug = tag2Name.toLowerCase(); | ||
let createForm2: CreateCommunityTag = { | ||
name: tag2Name, | ||
id_slug: tag2Slug, | ||
community_id: communityId, | ||
}; | ||
let tag2Res = await alpha.createCommunityTag(createForm2); | ||
expect(tag2Res.id).toBeDefined(); | ||
|
||
// Create a post | ||
let postRes = await alpha.createPost({ | ||
name: randomString(10), | ||
community_id: communityId, | ||
}); | ||
expect(postRes.post_view.post.id).toBeDefined(); | ||
|
||
// Update post tags | ||
let updateForm: UpdatePostTags = { | ||
post_id: postRes.post_view.post.id, | ||
tags: [tag1Res.id, tag2Res.id], | ||
}; | ||
let updateRes = await alpha.updatePostTags(updateForm); | ||
expect(updateRes.post_view.post.id).toBe(postRes.post_view.post.id); | ||
expect(updateRes.post_view.tags?.length).toBe(2); | ||
expect(updateRes.post_view.tags?.map(t => t.id).sort()).toEqual([tag1Res.id, tag2Res.id].sort()); | ||
|
||
// Update post to remove one tag | ||
updateForm.tags = [tag1Res.id]; | ||
updateRes = await alpha.updatePostTags(updateForm); | ||
expect(updateRes.post_view.post.id).toBe(postRes.post_view.post.id); | ||
expect(updateRes.post_view.tags?.length).toBe(1); | ||
expect(updateRes.post_view.tags?.[0].id).toBe(tag1Res.id); | ||
}); | ||
|
||
|
||
test("Post author can update post tags", async () => { | ||
// Create a community | ||
let communityRes = await createCommunity(alpha); | ||
const communityId = communityRes.community_view.community.id; | ||
|
||
// Create a tag | ||
const tagName = randomString(10); | ||
const tagSlug = tagName.toLowerCase(); | ||
let createForm: CreateCommunityTag = { | ||
name: tagName, | ||
id_slug: tagSlug, | ||
community_id: communityId, | ||
}; | ||
let tagRes = await alpha.createCommunityTag(createForm); | ||
expect(tagRes.id).toBeDefined(); | ||
|
||
let postRes = await createPost( | ||
alpha, | ||
communityId, | ||
"https://example.com/", | ||
"post with tags", | ||
); | ||
expect(postRes.post_view.post.id).toBeDefined(); | ||
|
||
// Alpha should be able to update tags on their own post | ||
let updateForm: UpdatePostTags = { | ||
post_id: postRes.post_view.post.id, | ||
tags: [tagRes.id], | ||
}; | ||
let updateRes = await alpha.updatePostTags(updateForm); | ||
expect(updateRes.post_view.post.id).toBe(postRes.post_view.post.id); | ||
expect(updateRes.post_view.tags?.length).toBe(1); | ||
expect(updateRes.post_view.tags?.[0].id).toBe(tagRes.id); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,5 @@ pub mod follow; | |
pub mod hide; | ||
pub mod pending_follows; | ||
pub mod random; | ||
pub mod tag; | ||
pub mod transfer; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
use activitypub_federation::config::Data; | ||
use actix_web::web::{Json, Query}; | ||
use lemmy_api_common::{ | ||
community::{ | ||
CommunityTagResponse, | ||
CreateCommunityTag, | ||
DeleteCommunityTag, | ||
ListCommunityTags, | ||
ListCommunityTagsResponse, | ||
UpdateCommunityTag, | ||
}, | ||
context::LemmyContext, | ||
utils::check_community_mod_action, | ||
}; | ||
use lemmy_db_schema::{ | ||
source::{ | ||
community::Community, | ||
tag::{Tag, TagInsertForm}, | ||
}, | ||
traits::Crud, | ||
}; | ||
use lemmy_db_views::structs::LocalUserView; | ||
use lemmy_utils::{error::LemmyResult, utils::validation::is_valid_tag_slug}; | ||
use url::Url; | ||
|
||
#[tracing::instrument(skip(context))] | ||
phiresky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub async fn create_community_tag( | ||
data: Json<CreateCommunityTag>, | ||
context: Data<LemmyContext>, | ||
local_user_view: LocalUserView, | ||
) -> LemmyResult<Json<CommunityTagResponse>> { | ||
let community = Community::read(&mut context.pool(), data.community_id).await?; | ||
|
||
// Verify that only mods can create tags | ||
check_community_mod_action( | ||
&local_user_view.person, | ||
&community, | ||
false, | ||
&mut context.pool(), | ||
) | ||
.await?; | ||
|
||
is_valid_tag_slug(&data.id_slug)?; | ||
|
||
// Create the tag | ||
let tag_form = TagInsertForm { | ||
name: data.name.clone(), | ||
community_id: data.community_id, | ||
ap_id: Url::parse(&format!("{}/tag/{}", community.actor_id, &data.id_slug))?.into(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add this as a function to the community impl? Then you can do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or better yet a db trigger |
||
published: None, // defaults to now | ||
updated: None, | ||
deleted: false, | ||
}; | ||
|
||
let tag = Tag::create(&mut context.pool(), &tag_form).await?; | ||
|
||
Ok(Json(CommunityTagResponse { | ||
id: tag.id, | ||
ap_id: tag.ap_id, | ||
name: tag.name, | ||
community_id: tag.community_id, | ||
})) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same concern as for the update. |
||
} | ||
|
||
#[tracing::instrument(skip(context))] | ||
pub async fn update_community_tag( | ||
data: Json<UpdateCommunityTag>, | ||
context: Data<LemmyContext>, | ||
local_user_view: LocalUserView, | ||
) -> LemmyResult<Json<CommunityTagResponse>> { | ||
let tag = Tag::read(&mut context.pool(), data.tag_id).await?; | ||
let community = Community::read(&mut context.pool(), tag.community_id).await?; | ||
|
||
// Verify that only mods can update tags | ||
check_community_mod_action( | ||
&local_user_view.person, | ||
&community, | ||
false, | ||
&mut context.pool(), | ||
) | ||
.await?; | ||
|
||
// Update the tag | ||
let tag_form = TagInsertForm { | ||
name: data.name.clone(), | ||
community_id: tag.community_id, | ||
ap_id: tag.ap_id, | ||
published: None, | ||
updated: Some(chrono::Utc::now()), | ||
deleted: false, | ||
phiresky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
let tag = Tag::update(&mut context.pool(), data.tag_id, &tag_form).await?; | ||
|
||
Ok(Json(CommunityTagResponse { | ||
id: tag.id, | ||
ap_id: tag.ap_id, | ||
name: tag.name, | ||
community_id: tag.community_id, | ||
})) | ||
phiresky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
#[tracing::instrument(skip(context))] | ||
pub async fn delete_community_tag( | ||
data: Json<DeleteCommunityTag>, | ||
context: Data<LemmyContext>, | ||
local_user_view: LocalUserView, | ||
) -> LemmyResult<Json<CommunityTagResponse>> { | ||
let tag = Tag::read(&mut context.pool(), data.tag_id).await?; | ||
let community = Community::read(&mut context.pool(), tag.community_id).await?; | ||
|
||
// Verify that only mods can delete tags | ||
check_community_mod_action( | ||
&local_user_view.person, | ||
&community, | ||
false, | ||
&mut context.pool(), | ||
) | ||
.await?; | ||
|
||
// Soft delete the tag | ||
let tag_form = TagInsertForm { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be a |
||
name: tag.name.clone(), | ||
community_id: tag.community_id, | ||
ap_id: tag.ap_id, | ||
published: None, | ||
updated: Some(chrono::Utc::now()), | ||
deleted: true, | ||
}; | ||
|
||
let tag = Tag::update(&mut context.pool(), data.tag_id, &tag_form).await?; | ||
|
||
Ok(Json(CommunityTagResponse { | ||
id: tag.id, | ||
ap_id: tag.ap_id, | ||
name: tag.name, | ||
community_id: tag.community_id, | ||
})) | ||
} | ||
|
||
#[tracing::instrument(skip(context))] | ||
pub async fn list_community_tags( | ||
phiresky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
data: Query<ListCommunityTags>, | ||
context: Data<LemmyContext>, | ||
) -> LemmyResult<Json<ListCommunityTagsResponse>> { | ||
let tags = Tag::get_by_community(&mut context.pool(), data.community_id).await?; | ||
|
||
let tag_responses = tags | ||
.into_iter() | ||
.map(|t| CommunityTagResponse { | ||
id: t.id, | ||
ap_id: t.ap_id, | ||
name: t.name, | ||
community_id: t.community_id, | ||
}) | ||
.collect(); | ||
phiresky marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Ok(Json(ListCommunityTagsResponse { | ||
tags: tag_responses, | ||
})) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ pub mod lock; | |
pub mod mark_many_read; | ||
pub mod mark_read; | ||
pub mod save; | ||
pub mod tags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would probably be better to split each action into its own file, like we've done with the other API actions.