Use custom types over text in database
This commit is contained in:
parent
5758c79f35
commit
4a9ba8e2a3
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1055,6 +1055,7 @@ name = "jirs-data"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"diesel",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"uuid 0.8.1",
|
"uuid 0.8.1",
|
||||||
|
@ -30,14 +30,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal > .clickableOverlay > .styledModal.center {
|
.modal > .clickableOverlay > .styledModal.center {
|
||||||
/*max-width: ${props => props.width}px;*/
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1);
|
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal > .clickableOverlay > .styledModal.aside {
|
.modal > .clickableOverlay > .styledModal.aside {
|
||||||
/*max-width: ${props => props.width}px;*/
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.15);
|
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,7 @@ impl Default for Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Icon {
|
pub enum Icon {
|
||||||
Bug,
|
Bug,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use jirs_data::{FullProject, Issue, IssuePriority, IssueType, UpdateIssuePayload};
|
use jirs_data::*;
|
||||||
|
|
||||||
use crate::model::{Icon, Model, Page};
|
use crate::model::{Icon, Model, Page};
|
||||||
use crate::shared::styled_avatar::StyledAvatar;
|
use crate::shared::styled_avatar::StyledAvatar;
|
||||||
@ -251,8 +251,6 @@ fn avatars_filters(model: &Model) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn project_board_lists(model: &Model) -> Node<Msg> {
|
fn project_board_lists(model: &Model) -> Node<Msg> {
|
||||||
use jirs_data::IssueStatus;
|
|
||||||
|
|
||||||
div![
|
div![
|
||||||
id!["projectBoardLists"],
|
id!["projectBoardLists"],
|
||||||
project_issue_list(model, IssueStatus::Backlog),
|
project_issue_list(model, IssueStatus::Backlog),
|
||||||
@ -317,28 +315,25 @@ fn project_issue(model: &Model, project: &FullProject, issue: &Issue) -> Node<Ms
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let issue_type_icon = match issue.issue_type.parse::<IssueType>() {
|
let issue_type_icon = {
|
||||||
Ok(icon) => {
|
let mut node = crate::shared::styled_icon(issue.issue_type.clone().into());
|
||||||
let mut node = crate::shared::styled_icon(icon.into());
|
|
||||||
node.add_style(
|
node.add_style(
|
||||||
St::Color,
|
St::Color,
|
||||||
format!("var(--{issue_type})", issue_type = issue.issue_type),
|
format!(
|
||||||
|
"var(--{issue_type})",
|
||||||
|
issue_type = issue.issue_type.to_string()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
node
|
node
|
||||||
}
|
|
||||||
Err(e) => span![format!("{}", e)],
|
|
||||||
};
|
};
|
||||||
let priority_icon = match issue.priority.parse::<IssuePriority>() {
|
let priority_icon = {
|
||||||
Ok(p) => {
|
let icon = match issue.priority {
|
||||||
let icon = match p {
|
|
||||||
IssuePriority::Low | IssuePriority::Lowest => Icon::ArrowDown,
|
IssuePriority::Low | IssuePriority::Lowest => Icon::ArrowDown,
|
||||||
_ => Icon::ArrowUp,
|
_ => Icon::ArrowUp,
|
||||||
};
|
};
|
||||||
let mut node = crate::shared::styled_icon(icon);
|
let mut node = crate::shared::styled_icon(icon);
|
||||||
node.add_style(St::Color, format!("var(--{})", p.to_lower_name()));
|
node.add_style(St::Color, format!("var(--{})", issue.priority));
|
||||||
node
|
node
|
||||||
}
|
|
||||||
Err(e) => span![e.clone()],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let issue_id = issue.id;
|
let issue_id = issue.id;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::prelude::*;
|
||||||
|
|
||||||
use crate::shared::inner_layout;
|
use crate::shared::inner_layout;
|
||||||
use crate::{model, Msg};
|
use crate::{model, Msg};
|
||||||
|
@ -4,6 +4,7 @@ use crate::model::Icon;
|
|||||||
use crate::shared::{styled_icon, ToNode};
|
use crate::shared::{styled_icon, ToNode};
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
|
||||||
pub enum Variant {
|
pub enum Variant {
|
||||||
Center,
|
Center,
|
||||||
@ -54,12 +55,16 @@ pub fn render(values: Modal) -> Node<Msg> {
|
|||||||
} else {
|
} else {
|
||||||
empty![]
|
empty![]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
|
||||||
|
let styled_modal_class = format!("styledModal {}", variant.to_class_name());
|
||||||
|
let styled_modal_style = format!("max-width: {width}px", width = width);
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "modal"],
|
attrs![At::Class => "modal"],
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => format!("clickableOverlay {}", variant.to_class_name())],
|
attrs![At::Class => clickable_class],
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => format!("styledModal {}", variant.to_class_name())],
|
attrs![At::Class => styled_modal_class, At::Style => styled_modal_style],
|
||||||
icon,
|
icon,
|
||||||
children
|
children
|
||||||
]
|
]
|
||||||
|
@ -4,6 +4,7 @@ use crate::model::Icon;
|
|||||||
use crate::shared::{styled_icon, ToNode};
|
use crate::shared::{styled_icon, ToNode};
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub enum Variant {
|
pub enum Variant {
|
||||||
Primary,
|
Primary,
|
||||||
Success,
|
Success,
|
||||||
|
@ -12,10 +12,16 @@ license = "MPL-2.0"
|
|||||||
name = "jirs_data"
|
name = "jirs_data"
|
||||||
path = "./src/lib.rs"
|
path = "./src/lib.rs"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
[features]
|
||||||
|
backend = [ "diesel" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "*"
|
serde = "*"
|
||||||
serde_json = "*"
|
serde_json = "*"
|
||||||
chrono = { version = "*", features = [ "serde" ] }
|
chrono = { version = "*", features = [ "serde" ] }
|
||||||
uuid = { version = ">=0.7.0, <0.9.0", features = ["serde"] }
|
uuid = { version = ">=0.7.0, <0.9.0", features = ["serde"] }
|
||||||
|
|
||||||
|
[dependencies.diesel]
|
||||||
|
optional = true
|
||||||
|
version = "1.4.4"
|
||||||
|
features = [ "unstable", "postgres", "numeric", "extras", "uuidv07" ]
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
#[cfg(feature = "backend")]
|
||||||
|
use diesel::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[cfg(feature = "backend")]
|
||||||
|
pub use sql::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "backend")]
|
||||||
|
pub mod sql;
|
||||||
|
|
||||||
pub trait ResponseData {
|
pub trait ResponseData {
|
||||||
type Response: Serialize;
|
type Response: Serialize;
|
||||||
|
|
||||||
fn into_response(self) -> Self::Response;
|
fn into_response(self) -> Self::Response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||||
|
#[cfg_attr(feature = "backend", sql_type = "IssueTypeType")]
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq)]
|
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum IssueType {
|
pub enum IssueType {
|
||||||
@ -31,14 +41,13 @@ impl FromStr for IssueType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for IssueType {
|
impl std::fmt::Display for IssueType {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
IssueType::Task => "Task",
|
IssueType::Task => f.write_str("task"),
|
||||||
IssueType::Bug => "Bug",
|
IssueType::Bug => f.write_str("bug"),
|
||||||
IssueType::Story => "Story",
|
IssueType::Story => f.write_str("story"),
|
||||||
}
|
}
|
||||||
.to_string()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +97,8 @@ impl IssueStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "backend", derive(FromSqlRow, AsExpression))]
|
||||||
|
#[cfg_attr(feature = "backend", sql_type = "IssuePriorityType")]
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq)]
|
#[derive(Clone, Deserialize, Serialize, Debug, PartialOrd, PartialEq)]
|
||||||
pub enum IssuePriority {
|
pub enum IssuePriority {
|
||||||
Highest,
|
Highest,
|
||||||
@ -112,6 +123,18 @@ impl FromStr for IssuePriority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for IssuePriority {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
IssuePriority::Highest => f.write_str("highest"),
|
||||||
|
IssuePriority::High => f.write_str("high"),
|
||||||
|
IssuePriority::Medium => f.write_str("medium"),
|
||||||
|
IssuePriority::Low => f.write_str("low"),
|
||||||
|
IssuePriority::Lowest => f.write_str("lowest"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IssuePriority {
|
impl IssuePriority {
|
||||||
pub fn to_text_value(&self) -> &str {
|
pub fn to_text_value(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
@ -123,16 +146,6 @@ impl IssuePriority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_lower_name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
IssuePriority::Highest => "highest",
|
|
||||||
IssuePriority::High => "high",
|
|
||||||
IssuePriority::Medium => "medium",
|
|
||||||
IssuePriority::Low => "low",
|
|
||||||
IssuePriority::Lowest => "lowest",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_value(&self) -> i32 {
|
pub fn to_value(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
IssuePriority::Highest => 5,
|
IssuePriority::Highest => 5,
|
||||||
@ -185,9 +198,9 @@ pub struct FullIssue {
|
|||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub issue_type: String,
|
pub issue_type: IssueType,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub priority: String,
|
pub priority: IssuePriority,
|
||||||
pub list_position: f64,
|
pub list_position: f64,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
@ -235,9 +248,9 @@ pub struct Issue {
|
|||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub issue_type: String,
|
pub issue_type: IssueType,
|
||||||
pub status: IssueStatus,
|
pub status: IssueStatus,
|
||||||
pub priority: String,
|
pub priority: IssuePriority,
|
||||||
pub list_position: f64,
|
pub list_position: f64,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
@ -293,9 +306,9 @@ pub struct Token {
|
|||||||
pub struct UpdateIssuePayload {
|
pub struct UpdateIssuePayload {
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub issue_type: Option<String>,
|
pub issue_type: Option<IssueType>,
|
||||||
pub status: Option<String>,
|
pub status: Option<String>,
|
||||||
pub priority: Option<String>,
|
pub priority: Option<IssuePriority>,
|
||||||
pub list_position: Option<f64>,
|
pub list_position: Option<f64>,
|
||||||
pub description: Option<Option<String>>,
|
pub description: Option<Option<String>>,
|
||||||
pub description_text: Option<Option<String>>,
|
pub description_text: Option<Option<String>>,
|
||||||
@ -327,9 +340,9 @@ pub struct UpdateCommentPayload {
|
|||||||
pub struct CreateIssuePayload {
|
pub struct CreateIssuePayload {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub issue_type: String,
|
pub issue_type: IssueType,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub priority: String,
|
pub priority: IssuePriority,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
pub estimate: Option<i32>,
|
pub estimate: Option<i32>,
|
||||||
|
81
jirs-data/src/sql.rs
Normal file
81
jirs-data/src/sql.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use diesel::{deserialize::*, pg::*, serialize::*, *};
|
||||||
|
|
||||||
|
use crate::{IssuePriority, IssueType};
|
||||||
|
|
||||||
|
#[derive(SqlType)]
|
||||||
|
#[postgres(type_name = "IssuePriorityType")]
|
||||||
|
pub struct IssuePriorityType;
|
||||||
|
|
||||||
|
impl ToSql<IssuePriorityType, Pg> for IssuePriority {
|
||||||
|
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
|
||||||
|
match *self {
|
||||||
|
IssuePriority::Highest => out.write_all(b"highest")?,
|
||||||
|
IssuePriority::High => out.write_all(b"high")?,
|
||||||
|
IssuePriority::Medium => out.write_all(b"medium")?,
|
||||||
|
IssuePriority::Low => out.write_all(b"low")?,
|
||||||
|
IssuePriority::Lowest => out.write_all(b"lowest")?,
|
||||||
|
}
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_priority_from_sql(bytes: Option<&[u8]>) -> deserialize::Result<IssuePriority> {
|
||||||
|
match not_none!(bytes) {
|
||||||
|
b"5" | b"highest" => Ok(IssuePriority::Highest),
|
||||||
|
b"4" | b"high" => Ok(IssuePriority::High),
|
||||||
|
b"3" | b"medium" => Ok(IssuePriority::Medium),
|
||||||
|
b"2" | b"low" => Ok(IssuePriority::Low),
|
||||||
|
b"1" | b"lowest" => Ok(IssuePriority::Lowest),
|
||||||
|
_ => Ok(IssuePriority::Lowest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql<IssuePriorityType, Pg> for IssuePriority {
|
||||||
|
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||||
|
issue_priority_from_sql(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql<sql_types::Text, Pg> for IssuePriority {
|
||||||
|
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||||
|
issue_priority_from_sql(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SqlType)]
|
||||||
|
#[postgres(type_name = "IssueTypeType")]
|
||||||
|
pub struct IssueTypeType;
|
||||||
|
|
||||||
|
fn issue_type_from_sql(bytes: Option<&[u8]>) -> deserialize::Result<IssueType> {
|
||||||
|
match not_none!(bytes) {
|
||||||
|
b"task" => Ok(IssueType::Task),
|
||||||
|
b"bug" => Ok(IssueType::Bug),
|
||||||
|
b"story" => Ok(IssueType::Story),
|
||||||
|
_ => Ok(IssueType::Task),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql<IssueTypeType, Pg> for IssueType {
|
||||||
|
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||||
|
issue_type_from_sql(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromSql<sql_types::Text, Pg> for IssueType {
|
||||||
|
fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
|
||||||
|
issue_type_from_sql(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql<IssueTypeType, Pg> for IssueType {
|
||||||
|
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
|
||||||
|
match *self {
|
||||||
|
IssueType::Task => out.write_all(b"task")?,
|
||||||
|
IssueType::Story => out.write_all(b"story")?,
|
||||||
|
IssueType::Bug => out.write_all(b"bug")?,
|
||||||
|
}
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,6 @@ name = "jirs_server"
|
|||||||
path = "./src/main.rs"
|
path = "./src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jirs-data = { path = "../jirs-data" }
|
|
||||||
serde = { version = "*", features = ["derive"] }
|
serde = { version = "*", features = ["derive"] }
|
||||||
actix = { version = "*" }
|
actix = { version = "*" }
|
||||||
actix-web = { version = "*" }
|
actix-web = { version = "*" }
|
||||||
@ -45,3 +44,6 @@ futures = { version = "*" }
|
|||||||
version = "1.4.4"
|
version = "1.4.4"
|
||||||
features = [ "unstable", "postgres", "numeric", "extras", "uuidv07" ]
|
features = [ "unstable", "postgres", "numeric", "extras", "uuidv07" ]
|
||||||
|
|
||||||
|
[dependencies.jirs-data]
|
||||||
|
path = "../jirs-data"
|
||||||
|
features = [ "backend" ]
|
||||||
|
@ -3,4 +3,5 @@
|
|||||||
|
|
||||||
[print_schema]
|
[print_schema]
|
||||||
file = "src/schema.rs"
|
file = "src/schema.rs"
|
||||||
|
import_types = ["diesel::sql_types::*", "jirs_data::sql::*"]
|
||||||
|
with_docs = true
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
DROP TYPE IF EXISTS ProjectCategory CASCADE;
|
DROP TYPE IF EXISTS "ProjectCategoryType" CASCADE;
|
||||||
|
DROP TYPE IF EXISTS "IssuePriorityType" CASCADE;
|
||||||
|
DROP TYPE IF EXISTS "IssueTypeType" CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS projects CASCADE;
|
DROP TABLE IF EXISTS projects CASCADE;
|
||||||
|
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
CREATE EXTENSION "uuid-ossp";
|
CREATE EXTENSION "uuid-ossp";
|
||||||
|
|
||||||
CREATE TYPE ProjectCategory as ENUM (
|
CREATE TYPE "ProjectCategoryType" as ENUM (
|
||||||
'software',
|
'software',
|
||||||
'marketing',
|
'marketing',
|
||||||
'business'
|
'business'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TYPE "IssuePriorityType" as ENUM (
|
||||||
|
'highest',
|
||||||
|
'high',
|
||||||
|
'medium',
|
||||||
|
'low',
|
||||||
|
'lowest'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TYPE "IssueTypeType" AS ENUM (
|
||||||
|
'task',
|
||||||
|
'bug',
|
||||||
|
'story'
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE projects (
|
CREATE TABLE projects (
|
||||||
id serial primary key not null,
|
id serial primary key not null,
|
||||||
name text not null,
|
name text not null,
|
||||||
@ -29,9 +43,9 @@ CREATE TABLE users (
|
|||||||
CREATE TABLE issues (
|
CREATE TABLE issues (
|
||||||
id serial primary key not null,
|
id serial primary key not null,
|
||||||
title text not null,
|
title text not null,
|
||||||
issue_type text not null,
|
issue_type "IssueTypeType" not null,
|
||||||
status text not null,
|
status text not null,
|
||||||
priority text not null,
|
priority "IssuePriorityType" not null,
|
||||||
list_position double precision not null default 0,
|
list_position double precision not null default 0,
|
||||||
description text,
|
description text,
|
||||||
description_text text,
|
description_text text,
|
||||||
@ -61,4 +75,3 @@ CREATE TABLE tokens (
|
|||||||
created_at timestamp not null default now(),
|
created_at timestamp not null default now(),
|
||||||
updated_at timestamp not null default now()
|
updated_at timestamp not null default now()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ insert into issues(
|
|||||||
), (
|
), (
|
||||||
'Foo3',
|
'Foo3',
|
||||||
'bug',
|
'bug',
|
||||||
'inprogress',
|
'in_progress',
|
||||||
'low',
|
'low',
|
||||||
3,
|
3,
|
||||||
'hello world 3',
|
'hello world 3',
|
||||||
|
@ -4,6 +4,8 @@ use diesel::expression::sql_literal::sql;
|
|||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use jirs_data::{IssuePriority, IssueType};
|
||||||
|
|
||||||
use crate::db::DbExecutor;
|
use crate::db::DbExecutor;
|
||||||
use crate::errors::ServiceErrors;
|
use crate::errors::ServiceErrors;
|
||||||
use crate::models::Issue;
|
use crate::models::Issue;
|
||||||
@ -66,9 +68,9 @@ impl Handler<LoadProjectIssues> for DbExecutor {
|
|||||||
pub struct UpdateIssue {
|
pub struct UpdateIssue {
|
||||||
pub issue_id: i32,
|
pub issue_id: i32,
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
pub issue_type: Option<String>,
|
pub issue_type: Option<IssueType>,
|
||||||
pub status: Option<String>,
|
pub status: Option<String>,
|
||||||
pub priority: Option<String>,
|
pub priority: Option<IssuePriority>,
|
||||||
pub list_position: Option<f64>,
|
pub list_position: Option<f64>,
|
||||||
pub description: Option<Option<String>>,
|
pub description: Option<Option<String>>,
|
||||||
pub description_text: Option<Option<String>>,
|
pub description_text: Option<Option<String>>,
|
||||||
@ -191,9 +193,9 @@ impl Handler<DeleteIssue> for DbExecutor {
|
|||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CreateIssue {
|
pub struct CreateIssue {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub issue_type: String,
|
pub issue_type: IssueType,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub priority: String,
|
pub priority: IssuePriority,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
pub estimate: Option<i32>,
|
pub estimate: Option<i32>,
|
||||||
|
@ -2,7 +2,8 @@ use chrono::NaiveDateTime;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use jirs_data::IssueStatus;
|
use jirs_data::sql::*;
|
||||||
|
use jirs_data::{IssuePriority, IssueStatus, IssueType};
|
||||||
|
|
||||||
use crate::schema::*;
|
use crate::schema::*;
|
||||||
|
|
||||||
@ -47,9 +48,9 @@ pub struct Issue {
|
|||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub issue_type: String,
|
pub issue_type: IssueType,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub priority: String,
|
pub priority: IssuePriority,
|
||||||
pub list_position: f64,
|
pub list_position: f64,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
@ -121,9 +122,9 @@ impl Into<jirs_data::FullIssue> for Issue {
|
|||||||
pub struct CreateIssueForm {
|
pub struct CreateIssueForm {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub issue_type: String,
|
pub issue_type: IssueType,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub priority: String,
|
pub priority: IssuePriority,
|
||||||
pub list_position: f64,
|
pub list_position: f64,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub description_text: Option<String>,
|
pub description_text: Option<String>,
|
||||||
|
@ -1,75 +1,341 @@
|
|||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `comments` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
comments (id) {
|
comments (id) {
|
||||||
|
/// The `id` column of the `comments` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
/// The `body` column of the `comments` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
body -> Text,
|
body -> Text,
|
||||||
|
/// The `user_id` column of the `comments` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
|
/// The `issue_id` column of the `comments` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
issue_id -> Int4,
|
issue_id -> Int4,
|
||||||
|
/// The `created_at` column of the `comments` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `comments` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `issue_assignees` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
issue_assignees (id) {
|
issue_assignees (id) {
|
||||||
|
/// The `id` column of the `issue_assignees` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
/// The `issue_id` column of the `issue_assignees` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
issue_id -> Int4,
|
issue_id -> Int4,
|
||||||
|
/// The `user_id` column of the `issue_assignees` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
|
/// The `created_at` column of the `issue_assignees` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `issue_assignees` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `issues` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
issues (id) {
|
issues (id) {
|
||||||
|
/// The `id` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
/// The `title` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
title -> Text,
|
title -> Text,
|
||||||
issue_type -> Text,
|
/// The `issue_type` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `IssueTypeType`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
issue_type -> IssueTypeType,
|
||||||
|
/// The `status` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
status -> Text,
|
status -> Text,
|
||||||
priority -> Text,
|
/// The `priority` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `IssuePriorityType`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
|
priority -> IssuePriorityType,
|
||||||
|
/// The `list_position` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Float8`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
list_position -> Float8,
|
list_position -> Float8,
|
||||||
|
/// The `description` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Nullable<Text>`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
description -> Nullable<Text>,
|
description -> Nullable<Text>,
|
||||||
|
/// The `description_text` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Nullable<Text>`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
description_text -> Nullable<Text>,
|
description_text -> Nullable<Text>,
|
||||||
|
/// The `estimate` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Nullable<Int4>`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
estimate -> Nullable<Int4>,
|
estimate -> Nullable<Int4>,
|
||||||
|
/// The `time_spent` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Nullable<Int4>`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
time_spent -> Nullable<Int4>,
|
time_spent -> Nullable<Int4>,
|
||||||
|
/// The `time_remaining` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Nullable<Int4>`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
time_remaining -> Nullable<Int4>,
|
time_remaining -> Nullable<Int4>,
|
||||||
|
/// The `reporter_id` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
reporter_id -> Int4,
|
reporter_id -> Int4,
|
||||||
|
/// The `project_id` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
project_id -> Int4,
|
project_id -> Int4,
|
||||||
|
/// The `created_at` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `issues` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `projects` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
projects (id) {
|
projects (id) {
|
||||||
|
/// The `id` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
/// The `name` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
/// The `url` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
url -> Text,
|
url -> Text,
|
||||||
|
/// The `description` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
description -> Text,
|
description -> Text,
|
||||||
|
/// The `category` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
category -> Text,
|
category -> Text,
|
||||||
|
/// The `created_at` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `projects` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
tokens (id) {
|
tokens (id) {
|
||||||
|
/// The `id` column of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
/// The `user_id` column of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
|
/// The `access_token` column of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Uuid`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
access_token -> Uuid,
|
access_token -> Uuid,
|
||||||
|
/// The `refresh_token` column of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Uuid`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
refresh_token -> Uuid,
|
refresh_token -> Uuid,
|
||||||
|
/// The `created_at` column of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `tokens` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use jirs_data::sql::*;
|
||||||
|
|
||||||
|
/// Representation of the `users` table.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
users (id) {
|
users (id) {
|
||||||
|
/// The `id` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
/// The `name` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
/// The `email` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Text`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
email -> Text,
|
email -> Text,
|
||||||
|
/// The `avatar_url` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Nullable<Text>`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
avatar_url -> Nullable<Text>,
|
avatar_url -> Nullable<Text>,
|
||||||
|
/// The `project_id` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Int4`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
project_id -> Int4,
|
project_id -> Int4,
|
||||||
|
/// The `created_at` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
/// The `updated_at` column of the `users` table.
|
||||||
|
///
|
||||||
|
/// Its SQL type is `Timestamp`.
|
||||||
|
///
|
||||||
|
/// (Automatically generated by Diesel.)
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,4 +349,11 @@ joinable!(issues -> users (reporter_id));
|
|||||||
joinable!(tokens -> users (user_id));
|
joinable!(tokens -> users (user_id));
|
||||||
joinable!(users -> projects (project_id));
|
joinable!(users -> projects (project_id));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(comments, issue_assignees, issues, projects, tokens, users,);
|
allow_tables_to_appear_in_same_query!(
|
||||||
|
comments,
|
||||||
|
issue_assignees,
|
||||||
|
issues,
|
||||||
|
projects,
|
||||||
|
tokens,
|
||||||
|
users,
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user