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

feat(matrix/space): implemented space.board events #16

Closed
wants to merge 16 commits into from

Conversation

avdb13
Copy link
Collaborator

@avdb13 avdb13 commented Jan 26, 2024

Stumbled upon some difficulties here since I wanted to do stuff correct without changing the API later.
I chose to separate post from reply to prevent having a separate topic event. Votes for reply are calculated through m.reaction but since it can form a relation with any event we can't enforce votes on this event type only.
I was not sure if we needed an enum for post since we might consider having different body types in the future such as a poll or media content.

Stumbled upon some difficulties here since I wanted to do stuff correct
without changing the API later.
I chose to separate `post` from `reply` to prevent having a separate
`topic` event. Votes for `reply` are calculated through `m.reaction` but
since it can form a relation with any event we can't enforce votes on
this event type only.
I was not sure if we needed an enum for `post` since we might consider
having different `body` types in the future such as a poll or media
content.
@avdb13 avdb13 force-pushed the events branch 2 times, most recently from c73b837 to b0fab96 Compare January 27, 2024 18:47
Access token is still absent since I'm still not sure how we will store
them. Another consideration to make is whether to require `room_id` for
replies since we *could* retrieve for the event being replied to and
extract it from there.
crates/core/src/db/entities/legacy.rs Outdated Show resolved Hide resolved
Comment on lines 144 to 168
pub async fn all_events(&self) -> Result<Vec<LegacyEntity>> {
let statement = "
SELECT
e.event_id,
js.json,
ms.display_name,
ms.avatar_url,
ra.room_alias,
RIGHT(e.event_id, 11) AS slug,
js.json :: json ->> 'origin_server_ts' AS edited_on,
0 AS replies,
FROM
events e
LEFT JOIN event_json js USING(event_id)
LEFT JOIN room_aliases ra USING(room_id)
LEFT JOIN event_relations er ON er.relates_to_id = e.event_id
LEFT JOIN redactions ON redacts = e.event_id
LEFT JOIN membership_state ms ON ms.user_id = e.sender
AND ms.room_id = e.room_id
WHERE
e.origin_server_ts < $1
AND e.type = 'space.board.post'
AND redacts IS NULL
AND aliases.room_alias IS NOT NULL
";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a way to achieve this via API? I would rather avoid querying database directly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is possible for Matrix events but our problem is that the response is paginated (see below).
I also decided to avoid plain SQL and go with sea_query instead as we wanna support SQLite and RocksDB in the future.
https://matrix-org.github.io/synapse/latest/admin_api/rooms.html

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can use filters and search terms for this perhaps?

Comment on lines 182 to 327
pub async fn all_replies(&self) -> Result<Vec<()>> {
unimplemented!()
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please avoid committing unimplemented functions.

@@ -1,15 +1,19 @@
pub mod account;
pub mod auth;
pub mod db;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Direct database access is not encouraged, unless core contains an owned DB I suggest using the Synapse API.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would use SQL Read Ops as last resort, its safer to use API as DAL.

Copy link
Collaborator Author

@avdb13 avdb13 Feb 5, 2024

Choose a reason for hiding this comment

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

No direct database access should be happening.

@avdb13 avdb13 force-pushed the events branch 2 times, most recently from f9271c6 to ef21bd9 Compare January 30, 2024 14:30
@avdb13
Copy link
Collaborator Author

avdb13 commented Feb 2, 2024

Excuses for not writing any tests yet by the way!

@@ -9,6 +9,7 @@ default-members = ["crates/server"]
resolver = "1"

[workspace.dependencies]
assert_matches2 = "0.1.2"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this required?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Comment on lines +46 to +56
let mut tmp = (*self.admin).clone();
tmp.set_token(access_token.to_string()).map_err(|err| {
tracing::error!(?err, "Failed to set access token");
Error::Unknown
})?;

tmp.put_json(path, &body).await.map_err(|err| {
tracing::error!(?err, "Failed to make http request");
Error::Unknown
})
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please specify error cause. Context is defined in the API

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Could you elaborate on context? The first error can only happen when the token is of the wrong format or empty, while the second one can be related to a wrong access token or malformed body.
Returning Error::Unknown here is not ideal, is that what you want to be changed?

pub async fn send_post<S: AsRef<str>>(
&self,
content: Raw<BoardPostEventContent>,
board_id: S,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
board_id: S,
board_id: impl AsRef<str>,

Given that this is the single param that uses generic value

Copy link
Collaborator

@EstebanBorai EstebanBorai left a comment

Choose a reason for hiding this comment

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

Thanks for giving this a shot!
I see this PR involves many features all at once in different layers of abstraction. I suggest split it into different PRs to address each concern at its own level.

I suggest using API calls over DB conns for DAL as well.

And please add tests, its important code is validated and that we can ensure it doesn't breaks with future changes!

I can see theres many types of events addressed all at once, perhaps adding the event abstraction to matrix crate is a good start for a PR, then introduce one of the event types and finally introduce it to the core logic so its presented (this could be a second PR) at this point testing against matrix and core logic its easy (refer to test crate for examples).

Keep it up!!

Comment on lines +77 to +83
let resp = self
.put_json(endpoint, &content, access_token)
.await
.map_err(|err| {
tracing::error!(?err, "Failed to forward custom event");
Error::Unknown
})?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please map API error codes

Comment on lines +85 to +88
let data: CreateEventResponse = resp.json().await.map_err(|err| {
tracing::error!(?err, "Failed deserialize Synapse response");
Error::Unknown
})?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Given that API version is pinned this shouldnt happen. Can you add tests for response bodies ad deserialization?

Comment on lines +31 to +34
pub const POSTGRES_HOST: &str = "POSTGRES_HOST";
pub const POSTGRES_USER: &str = "POSTGRES_USER";
pub const POSTGRES_PASSWORD: &str = "POSTGRES_PASSWORD";
pub const POSTGRES_DB: &str = "POSTGRES_DB";
Copy link
Collaborator

Choose a reason for hiding this comment

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

DB connection is not recommended. I suggest using API please.

use anyhow::Result;
use ruma_common::serde::Raw;
use ruma_common::{OwnedEventId, OwnedRoomId, TransactionId};

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change

Copy link
Collaborator Author

@avdb13 avdb13 Feb 5, 2024

Choose a reason for hiding this comment

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

We should have CI/CD set up for formatting.


#[derive(Serialize, Deserialize)]
pub struct CreateEventResponse {
event_id: String,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use newtype please

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you elaborate a bit on why is this called legacy, please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I already explained this ... it was to keep backward compatibility. Seems like it doesn't matter since it's only making implementation harder than necessary.

Comment on lines +30 to +38
let event = Router::new()
.route("/", get(unimplemented))
.route("/thread", get(unimplemented))
.route("/replies", get(unimplemented))
.merge(protected);

let events = Router::new()
.route("/", get(unimplemented))
.route("/:room", get(unimplemented));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Avoid pushing unimplemented code please.

Suggested change
let event = Router::new()
.route("/", get(unimplemented))
.route("/thread", get(unimplemented))
.route("/replies", get(unimplemented))
.merge(protected);
let events = Router::new()
.route("/", get(unimplemented))
.route("/:room", get(unimplemented));

@avdb13 avdb13 closed this Feb 5, 2024
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.

2 participants