Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
fixed bugs; delayed notification to publishment
Browse files Browse the repository at this point in the history
  • Loading branch information
thesuzerain committed Nov 2, 2023
1 parent 3edae49 commit 474ac88
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 106 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 12 additions & 9 deletions src/database/models/event_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use itertools::Itertools;
use sqlx::postgres::{PgHasArrayType, PgTypeInfo};
use std::convert::{TryFrom, TryInto};

#[derive(sqlx::Type, Clone, Copy)]
#[derive(sqlx::Type, Clone, Copy, Debug)]
#[sqlx(type_name = "text")]
#[sqlx(rename_all = "snake_case")]
pub enum EventType {
ProjectCreated,
ProjectPublished,
VersionCreated,
ProjectUpdated,
}
Expand All @@ -30,7 +30,7 @@ pub enum CreatorId {

#[derive(Debug)]
pub enum EventData {
ProjectCreated {
ProjectPublished {
project_id: ProjectId,
creator_id: CreatorId,
},
Expand Down Expand Up @@ -104,7 +104,7 @@ impl TryFrom<DynamicId> for CreatorId {
impl From<Event> for RawEvent {
fn from(value: Event) -> Self {
match value.event_data {
EventData::ProjectCreated {
EventData::ProjectPublished {
project_id,
creator_id,
} => {
Expand All @@ -116,7 +116,7 @@ impl From<Event> for RawEvent {
target_id_type: target_id.id_type,
triggerer_id: Some(triggerer_id.id),
triggerer_id_type: Some(triggerer_id.id_type),
event_type: EventType::ProjectCreated,
event_type: EventType::ProjectPublished,
metadata: None,
created: None,
}
Expand Down Expand Up @@ -175,11 +175,11 @@ impl TryFrom<RawEvent> for Event {
let event = Event {
id : value.id,
event_data : match value.event_type {
EventType::ProjectCreated => EventData::ProjectCreated {
EventType::ProjectPublished => EventData::ProjectPublished {
project_id: target_id.try_into()?,
creator_id: triggerer_id.map_or_else(|| {
Err(DatabaseError::UnexpectedNull(
"Neither triggerer_id nor triggerer_id_type should be null for project creation".to_string(),
"Neither triggerer_id nor triggerer_id_type should be null for project publishing".to_string(),
))
}, |v| v.try_into())?,
},
Expand Down Expand Up @@ -242,7 +242,8 @@ impl Event {
unzip_event_selectors(target_selectors);
let (triggerer_ids, triggerer_id_types, triggerer_event_types) =
unzip_event_selectors(triggerer_selectors);
sqlx::query_as!(

let r = sqlx::query_as!(
RawEvent,
r#"
SELECT
Expand Down Expand Up @@ -274,7 +275,9 @@ impl Event {
.await?
.into_iter()
.map(|r| r.try_into())
.collect::<Result<Vec<_>, _>>()
.collect::<Result<Vec<_>, _>>()?;

Ok(r)
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/database/models/project_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,16 @@ impl Project {
published, downloads, icon_url, issues_url,
source_url, wiki_url, status, requested_status, discord_url,
client_side, server_side, license_url, license,
slug, project_type, color, monetization_status
slug, project_type, color, monetization_status,
organization_id
)
VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8, $9,
$10, $11, $12, $13, $14,
$15, $16, $17, $18,
LOWER($19), $20, $21, $22
LOWER($19), $20, $21, $22,
$23
)
",
self.id as ProjectId,
Expand All @@ -331,6 +333,7 @@ impl Project {
self.project_type as ProjectTypeId,
self.color.map(|x| x as i32),
self.monetization_status.as_str(),
self.organization_id.map(|x| x.0),
)
.execute(&mut **transaction)
.await?;
Expand Down
26 changes: 26 additions & 0 deletions src/database/models/team_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,32 @@ impl TeamMember {
}
}

pub async fn get_owner_id<'a, 'b, E>(
id: TeamId,
executor: E,
) -> Result<Option<UserId>, super::DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let result = sqlx::query!(
"
SELECT user_id
FROM team_members
WHERE (team_id = $1 AND role = $2)
",
id as TeamId,
crate::models::teams::OWNER_ROLE,
)
.fetch_optional(executor)
.await?;

if let Some(m) = result {
Ok(Some(UserId(m.user_id)))
} else {
Ok(None)
}
}

pub async fn insert(
&self,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
Expand Down
2 changes: 1 addition & 1 deletion src/models/feeds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub struct FeedItem {
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum FeedItemBody {
ProjectCreated {
ProjectPublished {
project_id: ProjectId,
creator_id: CreatorId,
project_title: String,
Expand Down
50 changes: 24 additions & 26 deletions src/routes/v2/project_creation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::version_creation::InitialVersionData;
use crate::auth::{get_user_from_headers, AuthenticationError};
use crate::database::models::event_item::{CreatorId, Event, EventData};
use crate::database::models::thread_item::ThreadBuilder;
use crate::database::models::{self, image_item, User};
use crate::database::redis::RedisPool;
Expand All @@ -14,7 +13,7 @@ use crate::models::projects::{
DonationLink, License, MonetizationStatus, ProjectId, ProjectStatus, SideType, VersionId,
VersionStatus,
};
use crate::models::teams::ProjectPermissions;
use crate::models::teams::{OrganizationPermissions, ProjectPermissions};
use crate::models::threads::ThreadType;
use crate::models::users::UserId;
use crate::queue::session::AuthQueue;
Expand Down Expand Up @@ -453,6 +452,29 @@ async fn project_create_inner(
}
}

// If organization_id is set, make sure the user is a member of the organization
if let Some(organization_id) = create_data.organization_id {
let organization_team_member =
models::team_item::TeamMember::get_from_user_id_organization(
organization_id.into(),
current_user.id.into(),
&mut **transaction,
)
.await?;

let permissions = OrganizationPermissions::get_permissions_by_role(
&current_user.role,
&organization_team_member,
)
.unwrap_or_default();

if !permissions.contains(OrganizationPermissions::ADD_PROJECT) {
return Err(CreateError::CustomAuthenticationError(
"You do not have permission to add projects to this organization!".to_string(),
));
}
}

// Create VersionBuilders for the versions specified in `initial_versions`
versions = Vec::with_capacity(create_data.initial_versions.len());
for (i, data) in create_data.initial_versions.iter().enumerate() {
Expand Down Expand Up @@ -749,9 +771,6 @@ async fn project_create_inner(
}

let organization_id = project_create_data.organization_id.map(|id| id.into());
insert_project_create_event(project_id, organization_id, &current_user, transaction)
.await?;

let project_builder_actual = models::project_item::ProjectBuilder {
project_id: project_id.into(),
project_type_id,
Expand Down Expand Up @@ -893,27 +912,6 @@ async fn project_create_inner(
}
}

async fn insert_project_create_event(
project_id: ProjectId,
organization_id: Option<models::OrganizationId>,
current_user: &crate::models::users::User,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<(), CreateError> {
let event = Event::new(
EventData::ProjectCreated {
project_id: project_id.into(),
creator_id: organization_id.map_or_else(
|| CreatorId::User(current_user.id.into()),
CreatorId::Organization,
),
},
transaction,
)
.await?;
event.insert(transaction).await?;
Ok(())
}

async fn create_initial_version(
version_data: &InitialVersionData,
project_id: ProjectId,
Expand Down
71 changes: 55 additions & 16 deletions src/routes/v2/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::database::models::event_item::{CreatorId, EventData};
use crate::database::models::notification_item::NotificationBuilder;
use crate::database::models::project_item::{GalleryItem, ModCategory};
use crate::database::models::thread_item::ThreadMessageBuilder;
use crate::database::models::{image_item, Event};
use crate::database::models::{image_item, team_item, Event};
use crate::database::redis::RedisPool;
use crate::file_hosting::FileHost;
use crate::models;
Expand All @@ -25,13 +25,14 @@ use crate::util::routes::read_from_payload;
use crate::util::validate::validation_errors_to_string;
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use db_ids::OrganizationId;
use db_ids::{OrganizationId, UserId};
use futures::TryStreamExt;
use meilisearch_sdk::indexes::IndexesResults;
use serde::{Deserialize, Serialize};
use serde_json::json;
use sqlx::PgPool;
use std::sync::Arc;
use team_item::TeamMember;
use validator::Validate;

use database::models as db_models;
Expand Down Expand Up @@ -528,6 +529,21 @@ pub async fn project_edit(
)
.execute(&mut *transaction)
.await?;

// On publish event, we send out notification using team *owner* as publishing user, at the time of mod approval
// (even though 'user' is the mod and doing the publishing)
let owner_id =
TeamMember::get_owner_id(project_item.inner.team_id, &mut *transaction)
.await?;
if let Some(owner_id) = owner_id {
insert_project_publish_event(
id.into(),
project_item.inner.organization_id,
owner_id,
&mut transaction,
)
.await?;
}
}

if status.is_searchable() && !project_item.inner.webhook_sent {
Expand Down Expand Up @@ -1120,13 +1136,15 @@ pub async fn project_edit(
)
.await?;

insert_project_update_event(
id.into(),
project_item.inner.organization_id,
&user,
&mut transaction,
)
.await?;
if project_item.inner.status.is_searchable() {
insert_project_update_event(
project_item.inner.id.into(),
project_item.inner.organization_id,
&user,
&mut transaction,
)
.await?;
}

transaction.commit().await?;
Ok(HttpResponse::NoContent().body(""))
Expand Down Expand Up @@ -1477,13 +1495,15 @@ pub async fn projects_edit(
.await?;
}

insert_project_update_event(
project.inner.id.into(),
project.inner.organization_id,
&user,
&mut transaction,
)
.await?;
if project.inner.status.is_searchable() {
insert_project_update_event(
project.inner.id.into(),
project.inner.organization_id,
&user,
&mut transaction,
)
.await?;
}

db_models::Project::clear_cache(project.inner.id, project.inner.slug, None, &redis).await?;
}
Expand Down Expand Up @@ -2597,3 +2617,22 @@ async fn insert_project_update_event(
event.insert(transaction).await?;
Ok(())
}

async fn insert_project_publish_event(
project_id: ProjectId,
organization_id: Option<OrganizationId>,
owner_id: UserId,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<(), ApiError> {
let event = Event::new(
EventData::ProjectPublished {
project_id: project_id.into(),
creator_id: organization_id
.map_or_else(|| CreatorId::User(owner_id), CreatorId::Organization),
},
transaction,
)
.await?;
event.insert(transaction).await?;
Ok(())
}
1 change: 0 additions & 1 deletion src/routes/v2/version_creation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,6 @@ async fn insert_version_create_event(
current_user: &crate::models::users::User,
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<(), CreateError> {
println!("Adding version create event");
let event = Event::new(
EventData::VersionCreated {
version_id: version_id.into(),
Expand Down
Loading

0 comments on commit 474ac88

Please sign in to comment.