From b8868bc299dc89a2f184e7f17e7a9f39a60a8c43 Mon Sep 17 00:00:00 2001 From: Leandro Palazzolo Date: Sun, 19 Jan 2025 18:46:11 -0300 Subject: [PATCH 1/8] chore: migrate issues to tasks --- .../down.sql | 3 ++ .../2025-01-19-212212_issues-to-tasks/up.sql | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 migrations/2025-01-19-212212_issues-to-tasks/down.sql create mode 100644 migrations/2025-01-19-212212_issues-to-tasks/up.sql diff --git a/migrations/2025-01-19-212212_issues-to-tasks/down.sql b/migrations/2025-01-19-212212_issues-to-tasks/down.sql new file mode 100644 index 0000000..ba5aa26 --- /dev/null +++ b/migrations/2025-01-19-212212_issues-to-tasks/down.sql @@ -0,0 +1,3 @@ +DELETE FROM tasks +WHERE type = 'dev' + AND id IN (SELECT id FROM issues); \ No newline at end of file diff --git a/migrations/2025-01-19-212212_issues-to-tasks/up.sql b/migrations/2025-01-19-212212_issues-to-tasks/up.sql new file mode 100644 index 0000000..c626424 --- /dev/null +++ b/migrations/2025-01-19-212212_issues-to-tasks/up.sql @@ -0,0 +1,49 @@ +INSERT INTO tasks ( + id, + number, + repository_id, + title, + description, + labels, + open, + is_certified, + assignee_user_id, + issue_created_at, + issue_closed_at, + created_at, + updated_at, + type, + status, + url +) +SELECT + i.id, + i.number, + i.repository_id, + i.title, + i.description, + i.labels, + i.open, + COALESCE(i.certified, false) AS is_certified, + i.assignee_id AS assignee_user_id, + i.issue_created_at, + i.issue_closed_at, + i.created_at, + i.updated_at, + 'dev' AS type, + CASE + WHEN i.open THEN 'open' + ELSE 'completed' + END AS status, + CONCAT( + 'https://github.com/', + r.slug, + '/issues/', + i.number + ) AS url +FROM + issues i +JOIN + repositories r +ON + i.repository_id = r.id; \ No newline at end of file From bb9ef730cbcac34030f49ef4575748de22a06a16 Mon Sep 17 00:00:00 2001 From: Leandro Palazzolo Date: Sun, 19 Jan 2025 18:56:08 -0300 Subject: [PATCH 2/8] feat: add contributor role to existing users --- .../2025-01-19-212212_issues-to-tasks/down.sql | 6 +++++- migrations/2025-01-19-212212_issues-to-tasks/up.sql | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/migrations/2025-01-19-212212_issues-to-tasks/down.sql b/migrations/2025-01-19-212212_issues-to-tasks/down.sql index ba5aa26..2c4803c 100644 --- a/migrations/2025-01-19-212212_issues-to-tasks/down.sql +++ b/migrations/2025-01-19-212212_issues-to-tasks/down.sql @@ -1,3 +1,7 @@ DELETE FROM tasks WHERE type = 'dev' - AND id IN (SELECT id FROM issues); \ No newline at end of file + AND id IN (SELECT id FROM issues); + +DELETE FROM users_projects_roles +WHERE role_id = 2 + AND user_id IN (SELECT DISTINCT assignee_id FROM issues WHERE assignee_id IS NOT NULL); diff --git a/migrations/2025-01-19-212212_issues-to-tasks/up.sql b/migrations/2025-01-19-212212_issues-to-tasks/up.sql index c626424..17125f0 100644 --- a/migrations/2025-01-19-212212_issues-to-tasks/up.sql +++ b/migrations/2025-01-19-212212_issues-to-tasks/up.sql @@ -1,3 +1,5 @@ +-- Migrate issues to tasks + INSERT INTO tasks ( id, number, @@ -46,4 +48,12 @@ FROM JOIN repositories r ON - i.repository_id = r.id; \ No newline at end of file + i.repository_id = r.id; + +-- add CONTRIBUTOR role + +INSERT INTO users_projects_roles (user_id, role_id) +SELECT DISTINCT assignee_id AS user_id, 2 AS role_id +FROM issues +WHERE assignee_id IS NOT NULL +ON CONFLICT (user_id, project_id, role_id) DO NOTHING; From dc6246cea9fc3ef633d6cffcdcd6658251054943 Mon Sep 17 00:00:00 2001 From: Leandro Palazzolo Date: Sun, 19 Jan 2025 19:06:29 -0300 Subject: [PATCH 3/8] chore: add trigger to add contributor role --- .../down.sql | 2 ++ .../2025-01-19-215731_contributor-role/up.sql | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 migrations/2025-01-19-215731_contributor-role/down.sql create mode 100644 migrations/2025-01-19-215731_contributor-role/up.sql diff --git a/migrations/2025-01-19-215731_contributor-role/down.sql b/migrations/2025-01-19-215731_contributor-role/down.sql new file mode 100644 index 0000000..dd524fe --- /dev/null +++ b/migrations/2025-01-19-215731_contributor-role/down.sql @@ -0,0 +1,2 @@ +DROP TRIGGER IF EXISTS trigger_assign_role_to_user ON tasks; +DROP FUNCTION IF EXISTS assign_role_to_user; diff --git a/migrations/2025-01-19-215731_contributor-role/up.sql b/migrations/2025-01-19-215731_contributor-role/up.sql new file mode 100644 index 0000000..b4d3d7a --- /dev/null +++ b/migrations/2025-01-19-215731_contributor-role/up.sql @@ -0,0 +1,19 @@ +CREATE OR REPLACE FUNCTION assign_role_to_user() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO users_projects_roles (user_id, role_id) + SELECT NEW.assignee_user_id, 2 + WHERE NOT EXISTS ( + SELECT 1 + FROM users_projects_roles + WHERE user_id = NEW.assignee_user_id + AND role_id = 2 + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_assign_role_to_user +AFTER INSERT OR UPDATE OF assignee_user_id ON tasks +FOR EACH ROW +EXECUTE FUNCTION assign_role_to_user(); From e075c220238cfa71aca421105f278ffd6173836c Mon Sep 17 00:00:00 2001 From: Leandro Palazzolo Date: Sun, 19 Jan 2025 19:42:18 -0300 Subject: [PATCH 4/8] chore: update triggers --- .../2025-01-19-212212_issues-to-tasks/up.sql | 5 +++-- .../2025-01-19-223138_tasks-status/down.sql | 2 ++ migrations/2025-01-19-223138_tasks-status/up.sql | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 migrations/2025-01-19-223138_tasks-status/down.sql create mode 100644 migrations/2025-01-19-223138_tasks-status/up.sql diff --git a/migrations/2025-01-19-212212_issues-to-tasks/up.sql b/migrations/2025-01-19-212212_issues-to-tasks/up.sql index 17125f0..e892177 100644 --- a/migrations/2025-01-19-212212_issues-to-tasks/up.sql +++ b/migrations/2025-01-19-212212_issues-to-tasks/up.sql @@ -34,8 +34,9 @@ SELECT i.updated_at, 'dev' AS type, CASE - WHEN i.open THEN 'open' - ELSE 'completed' + WHEN i.open = TRUE AND i.assignee_id IS NULL THEN 'open' + WHEN i.open = TRUE AND i.assignee_id IS NOT NULL THEN 'in-progress' + WHEN i.open = FALSE THEN 'completed' END AS status, CONCAT( 'https://github.com/', diff --git a/migrations/2025-01-19-223138_tasks-status/down.sql b/migrations/2025-01-19-223138_tasks-status/down.sql new file mode 100644 index 0000000..61a86fe --- /dev/null +++ b/migrations/2025-01-19-223138_tasks-status/down.sql @@ -0,0 +1,2 @@ +DROP TRIGGER IF EXISTS update_task_status_trigger ON tasks; +DROP FUNCTION IF EXISTS update_task_status; \ No newline at end of file diff --git a/migrations/2025-01-19-223138_tasks-status/up.sql b/migrations/2025-01-19-223138_tasks-status/up.sql new file mode 100644 index 0000000..6393bd4 --- /dev/null +++ b/migrations/2025-01-19-223138_tasks-status/up.sql @@ -0,0 +1,16 @@ + +CREATE OR REPLACE FUNCTION update_task_status() RETURNS TRIGGER AS $$ +BEGIN + NEW.status := CASE + WHEN NEW.open = TRUE AND NEW.assignee_user_id IS NULL THEN 'open' + WHEN NEW.open = TRUE AND NEW.assignee_user_id IS NOT NULL THEN 'in-progress' + WHEN NEW.open = FALSE THEN 'completed' + END; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER update_task_status_trigger +BEFORE INSERT OR UPDATE ON tasks +FOR EACH ROW +EXECUTE FUNCTION update_task_status(); \ No newline at end of file From 2c6726e432d97901bfa9f897604c98030603be30 Mon Sep 17 00:00:00 2001 From: Connor Campbell Date: Tue, 28 Jan 2025 00:24:34 +0000 Subject: [PATCH 5/8] chore: add constraint to tasks table + trigger only on not null assignee --- .../2025-01-19-215731_contributor-role/up.sql | 18 ++++++++++-------- .../down.sql | 2 ++ .../up.sql | 2 ++ 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 migrations/2025-01-27-234215_add_unique_constraint_to_tasks/down.sql create mode 100644 migrations/2025-01-27-234215_add_unique_constraint_to_tasks/up.sql diff --git a/migrations/2025-01-19-215731_contributor-role/up.sql b/migrations/2025-01-19-215731_contributor-role/up.sql index b4d3d7a..c0bab5a 100644 --- a/migrations/2025-01-19-215731_contributor-role/up.sql +++ b/migrations/2025-01-19-215731_contributor-role/up.sql @@ -1,14 +1,16 @@ CREATE OR REPLACE FUNCTION assign_role_to_user() RETURNS TRIGGER AS $$ BEGIN - INSERT INTO users_projects_roles (user_id, role_id) - SELECT NEW.assignee_user_id, 2 - WHERE NOT EXISTS ( - SELECT 1 - FROM users_projects_roles - WHERE user_id = NEW.assignee_user_id - AND role_id = 2 - ); + IF NEW.assignee_user_id IS NOT NULL THEN + INSERT INTO users_projects_roles (user_id, role_id) + SELECT NEW.assignee_user_id, 2 + WHERE NOT EXISTS ( + SELECT 1 + FROM users_projects_roles + WHERE user_id = NEW.assignee_user_id + AND role_id = 2 + ); + END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; diff --git a/migrations/2025-01-27-234215_add_unique_constraint_to_tasks/down.sql b/migrations/2025-01-27-234215_add_unique_constraint_to_tasks/down.sql new file mode 100644 index 0000000..7f14109 --- /dev/null +++ b/migrations/2025-01-27-234215_add_unique_constraint_to_tasks/down.sql @@ -0,0 +1,2 @@ +ALTER TABLE tasks +DROP CONSTRAINT tasks_repo_number_unique; diff --git a/migrations/2025-01-27-234215_add_unique_constraint_to_tasks/up.sql b/migrations/2025-01-27-234215_add_unique_constraint_to_tasks/up.sql new file mode 100644 index 0000000..de54584 --- /dev/null +++ b/migrations/2025-01-27-234215_add_unique_constraint_to_tasks/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE tasks +ADD CONSTRAINT tasks_repo_number_unique UNIQUE (repository_id, number); From b375b3f0ff52a7ca2ae72c6f619cab48fdcf085d Mon Sep 17 00:00:00 2001 From: Leandro Palazzolo Date: Wed, 29 Jan 2025 18:46:49 -0300 Subject: [PATCH 6/8] chore: add migrations --- .../2025-01-29-212647_repository-id/down.sql | 3 +++ .../2025-01-29-212647_repository-id/up.sql | 6 ++++++ migrations/2025-01-29-212911_project-id/down.sql | 8 ++++++++ migrations/2025-01-29-212911_project-id/up.sql | 16 ++++++++++++++++ src/schema.rs | 15 +++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 migrations/2025-01-29-212647_repository-id/down.sql create mode 100644 migrations/2025-01-29-212647_repository-id/up.sql create mode 100644 migrations/2025-01-29-212911_project-id/down.sql create mode 100644 migrations/2025-01-29-212911_project-id/up.sql diff --git a/migrations/2025-01-29-212647_repository-id/down.sql b/migrations/2025-01-29-212647_repository-id/down.sql new file mode 100644 index 0000000..a3b0aa8 --- /dev/null +++ b/migrations/2025-01-29-212647_repository-id/down.sql @@ -0,0 +1,3 @@ + +ALTER TABLE tasks + DROP CONSTRAINT tasks_repository_id_fkey; diff --git a/migrations/2025-01-29-212647_repository-id/up.sql b/migrations/2025-01-29-212647_repository-id/up.sql new file mode 100644 index 0000000..f2d97f0 --- /dev/null +++ b/migrations/2025-01-29-212647_repository-id/up.sql @@ -0,0 +1,6 @@ + +ALTER TABLE tasks + ADD CONSTRAINT tasks_repository_id_fkey + FOREIGN KEY (repository_id) + REFERENCES repositories(id) + ON DELETE CASCADE; \ No newline at end of file diff --git a/migrations/2025-01-29-212911_project-id/down.sql b/migrations/2025-01-29-212911_project-id/down.sql new file mode 100644 index 0000000..dec2645 --- /dev/null +++ b/migrations/2025-01-29-212911_project-id/down.sql @@ -0,0 +1,8 @@ + +DROP TRIGGER IF EXISTS trigger_set_project_id ON tasks; +DROP FUNCTION IF EXISTS set_project_id_from_repository; +ALTER TABLE tasks + DROP CONSTRAINT tasks_repository_id_fkey; + +ALTER TABLE repositories + DROP CONSTRAINT unique_id_project_id; diff --git a/migrations/2025-01-29-212911_project-id/up.sql b/migrations/2025-01-29-212911_project-id/up.sql new file mode 100644 index 0000000..52a14c0 --- /dev/null +++ b/migrations/2025-01-29-212911_project-id/up.sql @@ -0,0 +1,16 @@ +ALTER TABLE repositories + ADD CONSTRAINT unique_id_project_id UNIQUE (id, project_id); + +CREATE OR REPLACE FUNCTION set_project_id_from_repository() +RETURNS TRIGGER AS $$ +BEGIN + NEW.project_id := (SELECT project_id FROM repositories WHERE id = NEW.repository_id); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_set_project_id +BEFORE INSERT OR UPDATE ON tasks +FOR EACH ROW +WHEN (NEW.repository_id IS NOT NULL AND NEW.project_id IS NULL) +EXECUTE FUNCTION set_project_id_from_repository(); \ No newline at end of file diff --git a/src/schema.rs b/src/schema.rs index c900f27..70ce5cf 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -28,6 +28,18 @@ diesel::table! { } } +diesel::table! { + milestones (id) { + id -> Int4, + slug -> Text, + name -> Text, + url -> Nullable, + project_id -> Int4, + created_at -> Timestamptz, + updated_at -> Nullable, + } +} + diesel::table! { projects (id) { id -> Int4, @@ -155,8 +167,10 @@ diesel::table! { diesel::joinable!(issues -> repositories (repository_id)); diesel::joinable!(issues -> users (assignee_id)); +diesel::joinable!(milestones -> projects (project_id)); diesel::joinable!(repositories -> projects (project_id)); diesel::joinable!(tasks -> projects (project_id)); +diesel::joinable!(tasks -> repositories (repository_id)); diesel::joinable!(tasks_votes -> tasks (task_id)); diesel::joinable!(tasks_votes -> users (user_id)); diesel::joinable!(team_memberships -> teams (team_id)); @@ -168,6 +182,7 @@ diesel::joinable!(users_projects_roles -> users (user_id)); diesel::allow_tables_to_appear_in_same_query!( issues, languages, + milestones, projects, repositories, roles, From ace3db1133a196375924eaa72b6fa8d0f493e6dd Mon Sep 17 00:00:00 2001 From: Connor Campbell Date: Thu, 30 Jan 2025 15:11:15 +0000 Subject: [PATCH 7/8] feat: add user to task response --- src/api/tasks/db.rs | 114 ++++++++++++++++++++++++++++++++++------ src/api/tasks/models.rs | 2 + 2 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/api/tasks/db.rs b/src/api/tasks/db.rs index 275ad70..faa8441 100644 --- a/src/api/tasks/db.rs +++ b/src/api/tasks/db.rs @@ -1,6 +1,7 @@ use diesel::prelude::*; use crate::schema::tasks::dsl as tasks_dsl; +use crate::schema::users::dsl as users_dsl; use crate::schema::tasks_votes::dsl as tasks_votes_dsl; use crate::db::{ @@ -9,15 +10,16 @@ use crate::db::{ }; use crate::types::PaginationParams; use crate::utils; +use crate::api::users::models::User; -use super::models::{NewTask, QueryParams, Task, TaskVote, TaskVoteDB, UpdateTask}; +use super::models::{NewTask, QueryParams, Task, TaskVote, TaskVoteDB, UpdateTask, TaskResponse}; pub trait DBTask: Send + Sync + Clone + 'static { fn all( &self, params: QueryParams, pagination: PaginationParams, - ) -> Result<(Vec, i64), DBError>; - fn by_id(&self, id: i32) -> Result, DBError>; + ) -> Result<(Vec, i64), DBError>; + fn by_id(&self, id: i32) -> Result, DBError>; fn create(&self, role: &NewTask) -> Result; fn update(&self, id: i32, role: &UpdateTask) -> Result; fn delete(&self, id: i32) -> Result<(), DBError>; @@ -30,7 +32,7 @@ impl DBTask for DBAccess { &self, params: QueryParams, pagination: PaginationParams, - ) -> Result<(Vec, i64), DBError> { + ) -> Result<(Vec, i64), DBError> { let conn = &mut self.get_db_conn(); let build_query = || { @@ -106,24 +108,106 @@ impl DBTask for DBAccess { let total_count = build_query().count().get_result::(conn)?; - let result = build_query() + let joined_query = build_query() + .left_join(users_dsl::users.on(tasks_dsl::assignee_user_id.eq(users_dsl::id.nullable()))) + .select(( + (tasks_dsl::tasks::all_columns()), + (users_dsl::users::all_columns().nullable()), + )) .order(tasks_dsl::created_at.desc()) .offset(pagination.offset) - .limit(pagination.limit) - .load::(conn)?; + .limit(pagination.limit); - Ok((result, total_count)) + let rows = joined_query.load::<(Task, Option)>(conn)?; + + let tasks_with_assignee = rows + .into_iter() + .map(|(task, user)| TaskResponse { + id: task.id, + number: task.number, + repository_id: task.repository_id, + title: task.title, + description: task.description, + url: task.url, + labels: task.labels, + open: task.open, + type_: task.type_, + project_id: task.project_id, + created_by_user_id: task.created_by_user_id, + assignee_user_id: task.assignee_user_id, + user, + assignee_team_id: task.assignee_team_id, + funding_options: task.funding_options, + contact: task.contact, + skills: task.skills, + bounty: task.bounty, + approved_by: task.approved_by, + approved_at: task.approved_at, + status: task.status, + upvotes: task.upvotes, + downvotes: task.downvotes, + is_featured: task.is_featured, + is_certified: task.is_certified, + featured_by_user_id: task.featured_by_user_id, + issue_created_at: task.issue_created_at, + issue_closed_at: task.issue_closed_at, + created_at: task.created_at, + updated_at: task.updated_at, + }) + .collect(); + + Ok((tasks_with_assignee, total_count)) } - fn by_id(&self, id: i32) -> Result, DBError> { + fn by_id(&self, id: i32) -> Result, DBError> { let conn = &mut self.get_db_conn(); - let result = tasks_dsl::tasks - .find(id) - .first::(conn) - .optional() - .map_err(DBError::from)?; - Ok(result) + + let row = tasks_dsl::tasks + .left_join( + users_dsl::users.on(tasks_dsl::assignee_user_id.eq(users_dsl::id.nullable())) + ) + .filter(tasks_dsl::id.eq(id)) + .select(( + (tasks_dsl::tasks::all_columns()), + (users_dsl::users::all_columns().nullable()), + )) + .first::<(Task, Option)>(conn) + .optional()?; + + Ok(row.map(|(task, user)| TaskResponse { + id: task.id, + number: task.number, + repository_id: task.repository_id, + title: task.title, + description: task.description, + url: task.url, + labels: task.labels, + open: task.open, + type_: task.type_, + project_id: task.project_id, + created_by_user_id: task.created_by_user_id, + assignee_user_id: task.assignee_user_id, + user, + assignee_team_id: task.assignee_team_id, + funding_options: task.funding_options, + contact: task.contact, + skills: task.skills, + bounty: task.bounty, + approved_by: task.approved_by, + approved_at: task.approved_at, + status: task.status, + upvotes: task.upvotes, + downvotes: task.downvotes, + is_featured: task.is_featured, + is_certified: task.is_certified, + featured_by_user_id: task.featured_by_user_id, + issue_created_at: task.issue_created_at, + issue_closed_at: task.issue_closed_at, + created_at: task.created_at, + updated_at: task.updated_at, + })) } + fn create(&self, task: &NewTask) -> Result { let conn = &mut self.get_db_conn(); diff --git a/src/api/tasks/models.rs b/src/api/tasks/models.rs index 6832a98..7290dab 100644 --- a/src/api/tasks/models.rs +++ b/src/api/tasks/models.rs @@ -1,6 +1,7 @@ use crate::schema::{tasks, tasks_votes}; use chrono::{DateTime, Utc}; use diesel::prelude::*; +use crate::api::users::models::User; use serde_derive::{Deserialize, Serialize}; // tasks @@ -134,6 +135,7 @@ pub struct TaskResponse { pub project_id: Option, pub created_by_user_id: Option, pub assignee_user_id: Option, + pub user: Option, // return the user if there is one already assigned pub assignee_team_id: Option, pub funding_options: Option>>, pub contact: Option, From 3f731476b32f1b906b06a0570f00e88d69d7fc62 Mon Sep 17 00:00:00 2001 From: Connor Campbell Date: Fri, 31 Jan 2025 10:59:08 +0000 Subject: [PATCH 8/8] chore: add repository and project to task response --- src/api/tasks/db.rs | 95 +++++++++++++++++++++++++++++++++++++---- src/api/tasks/models.rs | 2 + 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/api/tasks/db.rs b/src/api/tasks/db.rs index faa8441..40915d9 100644 --- a/src/api/tasks/db.rs +++ b/src/api/tasks/db.rs @@ -2,6 +2,8 @@ use diesel::prelude::*; use crate::schema::tasks::dsl as tasks_dsl; use crate::schema::users::dsl as users_dsl; +use crate::schema::repositories::dsl as repositories_dsl; +use crate::schema::projects::dsl as projects_dsl; use crate::schema::tasks_votes::dsl as tasks_votes_dsl; use crate::db::{ @@ -11,6 +13,9 @@ use crate::db::{ use crate::types::PaginationParams; use crate::utils; use crate::api::users::models::User; +use crate::api::projects::models::{ProjectResponse, Project}; +use crate::api::repositories::models::{RepositoryResponse, Repository}; + use super::models::{NewTask, QueryParams, Task, TaskVote, TaskVoteDB, UpdateTask, TaskResponse}; pub trait DBTask: Send + Sync + Clone + 'static { @@ -36,7 +41,21 @@ impl DBTask for DBAccess { let conn = &mut self.get_db_conn(); let build_query = || { - let mut query = tasks_dsl::tasks.into_boxed(); + let mut query = tasks_dsl::tasks + .left_join( + repositories_dsl::repositories + .on(tasks_dsl::repository_id.eq(repositories_dsl::id.nullable())) + ) + .inner_join( + projects_dsl::projects + .on(repositories_dsl::project_id.eq(projects_dsl::id)) + ) + .left_join( + users_dsl::users + .on(tasks_dsl::assignee_user_id.eq(users_dsl::id.nullable())) + ) + .into_boxed(); + if let Some(repository_id) = params.repository_id { query = query.filter(tasks_dsl::repository_id.eq(repository_id)); @@ -108,21 +127,22 @@ impl DBTask for DBAccess { let total_count = build_query().count().get_result::(conn)?; - let joined_query = build_query() - .left_join(users_dsl::users.on(tasks_dsl::assignee_user_id.eq(users_dsl::id.nullable()))) + let query = build_query() .select(( (tasks_dsl::tasks::all_columns()), + (repositories_dsl::repositories::all_columns().nullable()), + (projects_dsl::projects::all_columns()), (users_dsl::users::all_columns().nullable()), )) .order(tasks_dsl::created_at.desc()) .offset(pagination.offset) .limit(pagination.limit); - let rows = joined_query.load::<(Task, Option)>(conn)?; + let rows = query.load::<(Task, Option, Project, Option)>(conn)?; let tasks_with_assignee = rows .into_iter() - .map(|(task, user)| TaskResponse { + .map(|(task, repo, project, user)| TaskResponse { id: task.id, number: task.number, repository_id: task.repository_id, @@ -135,7 +155,28 @@ impl DBTask for DBAccess { project_id: task.project_id, created_by_user_id: task.created_by_user_id, assignee_user_id: task.assignee_user_id, - user, + user, + repository: repo.map(|r| RepositoryResponse { + id: r.id, + slug: r.slug, + name: r.name, + url: r.url, + language_slug: r.language_slug, + project: ProjectResponse { + id: project.id, + name: project.name, + slug: project.slug, + purposes: project.purposes, + stack_levels: project.stack_levels, + technologies: project.technologies, + avatar: project.avatar, + created_at: project.created_at, + updated_at: project.updated_at, + rewards: project.rewards, + }, + created_at: r.created_at, + updated_at: r.updated_at, + }), assignee_team_id: task.assignee_team_id, funding_options: task.funding_options, contact: task.contact, @@ -163,18 +204,32 @@ impl DBTask for DBAccess { let conn = &mut self.get_db_conn(); let row = tasks_dsl::tasks + .left_join( - users_dsl::users.on(tasks_dsl::assignee_user_id.eq(users_dsl::id.nullable())) + repositories_dsl::repositories + .on(tasks_dsl::repository_id.eq(repositories_dsl::id.nullable())) + ) + + .inner_join( + projects_dsl::projects + .on(repositories_dsl::project_id.eq(projects_dsl::id)) + ) + + .left_join( + users_dsl::users + .on(tasks_dsl::assignee_user_id.eq(users_dsl::id.nullable())) ) .filter(tasks_dsl::id.eq(id)) .select(( (tasks_dsl::tasks::all_columns()), + (repositories_dsl::repositories::all_columns().nullable()), + (projects_dsl::projects::all_columns()), (users_dsl::users::all_columns().nullable()), )) - .first::<(Task, Option)>(conn) + .first::<(Task, Option, Project, Option)>(conn) .optional()?; - Ok(row.map(|(task, user)| TaskResponse { + Ok(row.map(|(task, repo, project, user)| TaskResponse { id: task.id, number: task.number, repository_id: task.repository_id, @@ -205,9 +260,31 @@ impl DBTask for DBAccess { issue_closed_at: task.issue_closed_at, created_at: task.created_at, updated_at: task.updated_at, + repository: repo.map(|r| RepositoryResponse { + id: r.id, + slug: r.slug, + name: r.name, + url: r.url, + language_slug: r.language_slug, + project: ProjectResponse { + id: project.id, + name: project.name, + slug: project.slug, + purposes: project.purposes, + stack_levels: project.stack_levels, + technologies: project.technologies, + avatar: project.avatar, + created_at: project.created_at, + updated_at: project.updated_at, + rewards: project.rewards, + }, + created_at: r.created_at, + updated_at: r.updated_at, + }), })) } + fn create(&self, task: &NewTask) -> Result { let conn = &mut self.get_db_conn(); diff --git a/src/api/tasks/models.rs b/src/api/tasks/models.rs index 7290dab..b04358d 100644 --- a/src/api/tasks/models.rs +++ b/src/api/tasks/models.rs @@ -2,6 +2,7 @@ use crate::schema::{tasks, tasks_votes}; use chrono::{DateTime, Utc}; use diesel::prelude::*; use crate::api::users::models::User; +use crate::api::repositories::models::RepositoryResponse; use serde_derive::{Deserialize, Serialize}; // tasks @@ -136,6 +137,7 @@ pub struct TaskResponse { pub created_by_user_id: Option, pub assignee_user_id: Option, pub user: Option, // return the user if there is one already assigned + pub repository: Option, pub assignee_team_id: Option, pub funding_options: Option>>, pub contact: Option,