Skip to content
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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

Community post tags (part 2: API methods) #5389

wants to merge 17 commits into from

Conversation

phiresky
Copy link
Collaborator

@phiresky phiresky commented Feb 4, 2025

This is the second part of post tags, implementing the API methods.

Database PR (merged): #4997
Original RFC: LemmyNet/rfcs#4

@phiresky
Copy link
Collaborator Author

phiresky commented Feb 7, 2025

Ready for review. the API tests pass locally for me, but require merging LemmyNet/lemmy-js-client#483

Copy link
Member

@dessalines dessalines left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the slug thing about? The tag already has a name and an ap_id.

@dessalines
Copy link
Member

Do the re-request review when you want me to have another look. I thought about going through it but I'm guessing its still WIP.

@phiresky
Copy link
Collaborator Author

phiresky commented Feb 18, 2025

I've updated the PR

  • comments fixed
  • removed separate API methods, post create and update now do replacement of all tags (if tags given)
  • added tagupdateform

I'll merge main after reviews are green

@Nutomic
Copy link
Member

Nutomic commented Feb 20, 2025

Api tests are failing, looks like you need to merge changes from main branch here.

let tag_name_lower = tag_name.to_lowercase();
let id_slug = VALID_ID_SLUG.replace_all(&tag_name_lower, "-");
// limit length to 40 chars
let id_slug: String = id_slug.chars().take(40).collect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should limit length of the tag itself when it is created.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't know why id_slug needs to be separate from name. Aren't tags always just simple strings? If that's so, then its the tag name that needs to be validated before inserted.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how would you propose to construct the AP URL then?

Copy link
Member

@dessalines dessalines Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{domain}/tag/{name}, just like community and person.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name is a human readable and updateable name, with spaces, special characters, etc. not an id.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still it doesnt need a separate length limit, tag create/edit already has a limit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name is a human readable and updateable name, with spaces, special characters, etc. not an id.

Then we should adopt what's done for either person or community, on the tag table.

  • Have an unchanging, lowercase name column.
  • Have a title / display_name column, that can have spaces and other funky characters.

@phiresky
Copy link
Collaborator Author

i've temporarily set the lemmy-js-client lib to point to this pr LemmyNet/lemmy-js-client#483 so that the tests pass. please re-review

Comment on lines +27 to +29
if !(3..=100).contains(&length) {
return Err(LemmyErrorType::InvalidBodyField.into());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be externalized like all the others, and a specific error made for it. Check site_name_length_check for an example

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also there is no length check in update.

Comment on lines +44 to +46
published: None, // defaults to now
updated: None,
deleted: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

..Default::default(), or better, the insert form shouldn't need these at all, they already have defaults.

Check how PostInsertForm uses derive_new to do this.

// Update the tag
let tag_form = TagUpdateForm {
name: Some(data.name.clone()),
updated: Some(Some(chrono::Utc::now())),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be better to import this, so you can just use Utc::now() like all the other examples.

delete(post_tag::table.filter(post_tag::post_id.eq(post_id)))
.execute(conn)
.await?;
Ok(())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have the output of this be Result<usize, diesel::result::Error>, and delete this Ok(()) line.

.values(forms)
.execute(conn)
.await?;
Ok(())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, delete this Ok(()) line, and have the output be Result<Vec<Self>, ..

use crate::schema::tag::dsl::*;
let conn = &mut get_conn(pool).await?;
tag
.filter(community_id.eq(search_community_id))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag::community_id

) -> Result<Vec<Self>, Error> {
use crate::schema::tag::dsl::*;
let conn = &mut get_conn(pool).await?;
tag
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag::table

let conn = &mut get_conn(pool).await?;
tag
.filter(community_id.eq(search_community_id))
.filter(deleted.eq(false))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag::deleted

tag
.filter(community_id.eq(search_community_id))
.filter(deleted.eq(false))
.load::<Tag>(conn)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self

Comment on lines +631 to +640
#[cfg(feature = "full")]
#[diesel::dsl::auto_type]
fn tag_fragment() -> _ {
let sel: SqlLiteral<Json> = diesel::dsl::sql::<diesel::sql_types::Json>("json_agg(tag.*)");
tag::table
.select(sel)
.filter(tag::community_id.eq(community::id))
.filter(tag::deleted.eq(false))
.single_value()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thx that's going to make things a lot easier.

})
.collect(),
)
.await?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge these into a single method PostTag::set() which deletes old tags and sets new ones in a transaction.

.await?;
}
// validate tags
let valid_tags: std::collections::HashSet<TagId> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import Hashset

Ok(Json(tag))
}

#[tracing::instrument(skip(context))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove these macros, see #5368


// List tags
let listRes = await alpha.getCommunity({ id: communityId });
expect(listRes.community_view.post_tags.length).toBeGreaterThan(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(listRes.community_view.post_tags.length).toBeGreaterThan(0);
expect(listRes.community_view.post_tags.length).toBe(1);

Comment on lines +62 to +64
expect(
listRes.community_view.post_tags.find(t => t.id === createRes.id),
).toBeUndefined();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(
listRes.community_view.post_tags.find(t => t.id === createRes.id),
).toBeUndefined();
expect(listRes.community_view.post_tags.length).toBe(0);

@Nutomic
Copy link
Member

Nutomic commented Feb 28, 2025

@phiresky The discussion at the top about table names is not resolved yet. If you have better ideas how to name them thats fine, but right now the names dont make it clear what these tables and structs are for.

#5389 (comment)

@phiresky
Copy link
Collaborator Author

phiresky commented Feb 28, 2025

@Nutomic the discussion you linked is about the API path, which is resolved. Tables are not even part of this PR.

Copy link
Member

@Nutomic Nutomic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the tables were in #4997 which is part of the same feature. In that PR it wasnt clear yet how it all works, only now is it possible to review properly. So Im repeating my request again:

Yes it all needs to be made more consistent. For the main tags table community_tags makes sense, while the relation tag-post can be represented by a table community_tags_for_post.

@dessalines
Copy link
Member

dessalines commented Feb 28, 2025

Here's that discussion, and I believe that we agreed on leaving a generic tag table, and validating that its an allowed tag in the back-end. (And possibly adding a tag_type enum in the future). Any specific items then get a _tag table, like here for post_tag.

So I don't believe any tag table changes (or new tables) are necessary, outside of possibly adding a display_name column.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants