milestone.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. use std::str::FromStr;
  2. use askama::Template;
  3. use serde::{Deserialize, Serialize};
  4. use sqlx::pool::PoolConnection;
  5. use sqlx::Postgres;
  6. use sqlx::postgres::PgQueryResult;
  7. use uuid::Uuid;
  8. use tide::{http::mime, Request};
  9. use crate::home::NotFoundTemplate;
  10. use crate::matrix::post_milestone_create;
  11. use crate::{State, user};
  12. // SQL STUFF
  13. pub async fn get_milestones_by_project(conn: &mut PoolConnection<Postgres>, project_key: uuid::Uuid) -> Vec::<Milestone> {
  14. let milestone_records = sqlx::query!(
  15. "select key, organization_key, owner_key, project_key, name, description, tags, estimated_quarter_days, start, due, created, updated from mile_stones where project_key = $1",
  16. project_key
  17. )
  18. .fetch_all(conn)
  19. .await
  20. .expect("Select milestone by key");
  21. let mut milestones = vec![];
  22. for milestone in milestone_records {
  23. milestones.push(Milestone {
  24. key: milestone.key.expect("key exists"),
  25. organization_key: milestone.organization_key.expect("organization_key"),
  26. owner_key: milestone.owner_key.expect("owner_key exists"),
  27. project_key: milestone.project_key.expect("project_key exists"),
  28. name: milestone.name.expect("name exists"),
  29. description: milestone.description.expect("description exists"),
  30. tags: milestone.tags.expect("tags exists"),
  31. estimated_quarter_days: milestone.estimated_quarter_days.expect("estimated_quarter_days exists"),
  32. start: milestone.start.expect("start"),
  33. due:milestone.due.expect("due"),
  34. created: milestone.created.expect("created exists"),
  35. updated: milestone.updated.expect("updated exists"),
  36. })
  37. }
  38. milestones
  39. }
  40. pub async fn get_milestone(conn: &mut PoolConnection<Postgres>, key: uuid::Uuid) -> Milestone {
  41. let milestone = sqlx::query!(
  42. "select key, organization_key, owner_key, project_key, name, description, tags, estimated_quarter_days, start, due, created, updated from mile_stones where key = $1",
  43. key
  44. )
  45. .fetch_one(conn)
  46. .await
  47. .expect("Select milestone by key");
  48. Milestone {
  49. key: milestone.key.expect("key exists"),
  50. organization_key: milestone.organization_key.expect("organization_key"),
  51. owner_key: milestone.owner_key.expect("owner_key exists"),
  52. project_key: milestone.project_key.expect("project_key exists"),
  53. name: milestone.name.expect("name exists"),
  54. description: milestone.description.expect("description exists"),
  55. tags: milestone.tags.expect("tags exists"),
  56. estimated_quarter_days: milestone.estimated_quarter_days.expect("estimated_quarter_days exists"),
  57. start: milestone.start.expect("start"),
  58. due:milestone.due.expect("due"),
  59. created: milestone.created.expect("created exists"),
  60. updated: milestone.updated.expect("updated exists"),
  61. }
  62. }
  63. async fn delete_milestone(conn: &mut PoolConnection<Postgres>, key: uuid::Uuid, owner_key: uuid::Uuid) -> Result<PgQueryResult, sqlx::Error> {
  64. return sqlx::query!("DELETE FROM mile_stones where owner_key=$1 AND key=$2", owner_key, key)
  65. .execute(conn)
  66. .await;
  67. }
  68. async fn update_milestone(conn: &mut PoolConnection<Postgres>, board: &Milestone) {
  69. sqlx::query!("UPDATE mile_stones SET name=$1, description=$2, tags=$3,
  70. estimated_quarter_days=$4, start=$5, due=$6 where key=$7",
  71. &board.name,
  72. &board.description,
  73. &board.tags,
  74. &board.estimated_quarter_days,
  75. &board.start,
  76. &board.due,
  77. board.key,
  78. )
  79. .execute(conn)
  80. .await
  81. .expect("Insert Success");
  82. }
  83. async fn insert_milestone(conn: &mut PoolConnection<Postgres>, new_milestone: &Milestone) {
  84. sqlx::query!("INSERT INTO mile_stones (key, organization_key, project_key, owner_key, name, description, tags, estimated_quarter_days, start, due, created, updated) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
  85. new_milestone.key,
  86. new_milestone.organization_key,
  87. new_milestone.project_key,
  88. new_milestone.owner_key,
  89. &new_milestone.name,
  90. &new_milestone.description,
  91. &new_milestone.tags,
  92. new_milestone.estimated_quarter_days,
  93. new_milestone.start,
  94. new_milestone.due,
  95. new_milestone.created,
  96. new_milestone.updated,
  97. )
  98. .execute(conn)
  99. .await
  100. .expect("Insert Success");
  101. }
  102. // Route Stuff
  103. pub async fn delete(req: Request<State>) -> tide::Result {
  104. let u = match crate::user::user_or_error(&req) {
  105. Ok(value) => value,
  106. Err(_) => return Ok(tide::Redirect::new("/login").into()),
  107. };
  108. match req.param("milestone_id") {
  109. Ok(key) => {
  110. let mut conn = req.state().db_pool.acquire().await?;
  111. let s_uuid = uuid::Uuid::from_str(key).expect("Milestone uuid parse");
  112. match delete_milestone(&mut conn, s_uuid, u.key).await {
  113. Ok(_) => Ok(tide::Response::builder(tide::StatusCode::Ok)
  114. .content_type(mime::HTML)
  115. .build()),
  116. Err(_) => Ok(tide::Response::builder(tide::StatusCode::InternalServerError)
  117. .content_type(mime::HTML)
  118. .body(NotFoundTemplate::new().render_string())
  119. .build()),
  120. }
  121. }
  122. Err(e) => {
  123. println!("{:?}", e);
  124. Ok(tide::Response::builder(tide::StatusCode::NotFound)
  125. .content_type(mime::HTML)
  126. .body(NotFoundTemplate::new().render_string())
  127. .build())
  128. }
  129. }
  130. }
  131. pub async fn get(req: Request<State>) -> tide::Result {
  132. let u = match crate::user::read_jwt_cookie_to_user(req.cookie("token")) {
  133. Some(c) => c,
  134. None => {
  135. return Ok(tide::Redirect::new("/login").into());
  136. }
  137. };
  138. match req.param("milestone_id") {
  139. Ok(key) => {
  140. let mut conn = req.state().db_pool.acquire().await?; // .await? needs to be a real connection pool error handler!!!!!!!!
  141. let s_uuid = uuid::Uuid::from_str(key).expect("Milestone uuid parse");
  142. let milestone = get_milestone(&mut conn, s_uuid).await;
  143. Ok(tide::Response::builder(tide::StatusCode::Ok)
  144. .content_type(mime::HTML)
  145. .body(
  146. MilestoneTemplate::new(
  147. milestone,
  148. u,
  149. )
  150. .render_string(),
  151. )
  152. .build())
  153. }
  154. Err(e) => {
  155. println!("{:?}", e);
  156. Ok(tide::Response::builder(tide::StatusCode::NotFound)
  157. .content_type(mime::HTML)
  158. .body(NotFoundTemplate::new().render_string())
  159. .build())
  160. }
  161. }
  162. }
  163. pub async fn insert(mut req: Request<State>) -> tide::Result {
  164. let umd: Result<Milestone, tide::Error> = req.body_json().await;
  165. let claims: user::UserJwtState = match user::read_jwt_cookie(req.cookie("token")) {
  166. Some(c) => c,
  167. None => {
  168. return Ok(tide::Redirect::new("/login").into());
  169. },
  170. };
  171. match umd {
  172. Ok(milestone) => {
  173. let mut conn = match req.state().db_pool.acquire().await {
  174. Ok(c) => c,
  175. Err(e) => {
  176. return Ok(
  177. tide::Response::builder(tide::StatusCode::InternalServerError)
  178. .content_type(mime::PLAIN)
  179. .body(e.to_string())
  180. .build(),
  181. )
  182. }
  183. };
  184. if milestone.key == uuid::Uuid::nil() {
  185. let s = Milestone::new(milestone.organization_key, milestone.owner_key, milestone.project_key, milestone.name, milestone.description, milestone.tags, milestone.estimated_quarter_days, milestone.start, milestone.due);
  186. insert_milestone(&mut conn, &s).await;
  187. let organization_key = uuid::Uuid::from_str(claims.organization_key.as_str()).expect("organization key");
  188. post_milestone_create(&mut conn, claims.matrix_home_server, claims.matrix_user_id, organization_key, claims.matrix_access_token, &s).await.expect("Posting to matrix");
  189. let j = serde_json::to_string(&s).expect("To JSON");
  190. Ok(tide::Response::builder(tide::StatusCode::Ok)
  191. .content_type(mime::JSON)
  192. .body(j)
  193. .build())
  194. } else {
  195. update_milestone(&mut conn, &milestone).await;
  196. let j = serde_json::to_string(&milestone).expect("To JSON");
  197. return Ok(tide::Response::builder(tide::StatusCode::Ok)
  198. .content_type(mime::JSON)
  199. .body(j)
  200. .build())
  201. }
  202. }
  203. Err(e) => {
  204. println!("{:?}", e);
  205. Ok(tide::Response::builder(tide::StatusCode::BadRequest)
  206. .content_type(mime::JSON)
  207. .body("{'error': 'invalid json body'}")
  208. .build())
  209. }
  210. }
  211. }
  212. pub async fn add(req: Request<State>) -> tide::Result {
  213. let u = match crate::user::user_or_error(&req) {
  214. Ok(value) => value,
  215. Err(_) => return Ok(tide::Redirect::new("/login").into()),
  216. };
  217. match req.param("project_id") {
  218. Ok(project_id) => {
  219. let project_id = uuid::Uuid::from_str(project_id).expect("Project uuid parse");
  220. let mut milestone= Milestone::new( u.organization_key, u.key, project_id, "".to_owned(), "".to_owned(), "".to_owned(), 0, 0, 0);
  221. milestone.key = uuid::Uuid::nil();
  222. Ok(tide::Response::builder(tide::StatusCode::Ok)
  223. .content_type(mime::HTML)
  224. .body(
  225. MilestoneTemplate::new(
  226. milestone,
  227. u,
  228. )
  229. .render_string(),
  230. )
  231. .build())
  232. }
  233. Err(e) => {
  234. println!("{:?}", e);
  235. Ok(tide::Response::builder(tide::StatusCode::NotFound)
  236. .content_type(mime::HTML)
  237. .body(NotFoundTemplate::new().render_string())
  238. .build())
  239. }
  240. }
  241. }
  242. // data types
  243. #[derive(Debug, Deserialize, Serialize)]
  244. pub struct Milestone {
  245. pub key: uuid::Uuid,
  246. pub organization_key: uuid::Uuid,
  247. pub owner_key: uuid::Uuid,
  248. pub project_key: uuid::Uuid,
  249. pub name: String,
  250. pub description: String,
  251. pub tags: String,
  252. pub estimated_quarter_days: i32,
  253. pub start: i64,
  254. pub due: i64,
  255. pub created: i64,
  256. pub updated: i64,
  257. }
  258. impl Milestone {
  259. pub fn new(
  260. organization_key: uuid::Uuid,
  261. owner_key: uuid::Uuid,
  262. project_key: uuid::Uuid,
  263. name: String,
  264. description: String,
  265. tags: String,
  266. estimated_quarter_days:i32,
  267. start: i64,
  268. due: i64,
  269. ) -> Self {
  270. let key = Uuid::new_v4();
  271. let created = chrono::Utc::now().timestamp();
  272. let updated = 0;
  273. Self {
  274. key,
  275. organization_key,
  276. project_key,
  277. owner_key,
  278. name,
  279. description,
  280. tags,
  281. estimated_quarter_days,
  282. start,
  283. due,
  284. created,
  285. updated,
  286. }
  287. }
  288. }
  289. #[derive(Template)]
  290. #[template(path = "milestone.html")]
  291. pub struct MilestoneTemplate {
  292. milestone: Milestone,
  293. user: crate::user::User,
  294. }
  295. impl<'a> MilestoneTemplate {
  296. pub fn new(
  297. milestone: Milestone,
  298. user: crate::user::User,
  299. ) -> Self {
  300. return Self {
  301. milestone,
  302. user,
  303. };
  304. }
  305. pub fn render_string(&self) -> String {
  306. return self.render().unwrap();
  307. }
  308. }