Skip to content

Commit

Permalink
Merge pull request #22 from OneLiteFeatherNET/feat/responses
Browse files Browse the repository at this point in the history
Feat/responses
  • Loading branch information
Randoooom committed Dec 30, 2023
2 parents 3953e49 + 0389c75 commit 863f4c5
Show file tree
Hide file tree
Showing 19 changed files with 1,372 additions and 138 deletions.
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
argon2 = "0.5.2"
async-trait = "0.1.74"
axum = "0.6.20"
chrono = { version = "0.4.31", features = ["serde"] }
Expand All @@ -27,7 +26,6 @@ serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
thiserror = "1.0.50"
tokio = { version = "1.33.0", features = ["full"] }
totp-rs = { version = "5.4.0", features = ["qr", "gen_secret"] }
tower = { version = "0.4.13", features = ["limit", "buffer"] }
tower-http = { version = "0.4.4", features = ["trace"] }
tracing = "0.1.39"
Expand All @@ -37,6 +35,12 @@ utoipa = { version = "4.1.0", features = ["yaml", "chrono"] }
validator = { version = "0.16", features = ["derive"] }
version-compare = "0.1.1"

[dev-dependencies]
rand = "0.8.5"
reqwest = { version = "0.11.23", features = ["json"] }
test-log = "0.2.14"
openidconnect = "3.4.0"

[features]
default = ["all-databases"]

Expand All @@ -45,3 +49,5 @@ all-databases = ["postgres", "mysql"]
postgres = ["rbdc-pg"]
mysql = ["rbdc-mysql"]

test = []

27 changes: 23 additions & 4 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,26 @@ script = "docker compose -f testing/oidc-mock/docker-compose.yaml up -d"
script = "docker run --name postgres -e POSTGRES_PASSWORD=password -e POSTGRES_USERNAME=postgres -p 5150:5432 -d postgres && sleep 1"

[tasks.postgres_tests]
env = { postgres_username = "postgres", postgres_password = "password", postgres_endpoint = "localhost:5150", postgres_database = "postgres", "oidc_discovery_url" = "http://localhost:5151/" }
env = { DATABASE = "POSTGRES", POSTGRES_USERNAME = "postgres", POSTGRES_PASSWORD = "password", POSTGRES_ENDPOINT = "localhost:5150", POSTGRES_DATABASE = "postgres", "OIDC_DISCOVERY_URL" = "http://localhost:5151", OIDC_CLIENT_ID = "client", OIDC_CLIENT_SECRET = "secret", RUST_LOG = "debug" }
command = "cargo"
args = ["test", "--features", "postgres"]
args = [
"test",
"--no-default-features",
"--features",
"postgres,test",
"--test",
"http_tests",
"--",
"--nocapture",
"--test-threads=1"
]

[tasks.postgres]
run_task = { name = ["oidc-server-mock", "postgres_database", "postgres_tests"], fork = true, cleanup_task = "postgres_cleanup"}
run_task = { name = [
"oidc-server-mock",
"postgres_database",
"postgres_tests",
], fork = true, cleanup_task = "postgres_cleanup" }

[tasks.postgres_cleanup]
script = "docker kill postgres;docker rm postgres;docker kill oidc-server-mock;docker rm oidc-server-mock"
Expand All @@ -53,7 +67,12 @@ run_task = { name = ["postgres"] }
[tasks.docs_lint]
dependencies = ["docs_generate"]
command = "npx"
args = ["redocly", "lint", "--skip-rule=no-empty-servers", "target/openapi.yaml"]
args = [
"redocly",
"lint",
"--skip-rule=no-empty-servers",
"target/openapi.yaml",
]

[tasks.docs_build]
dependencies = ["docs_lint"]
Expand Down
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// just placeholder to get OUT_DIR

fn main() {}

8 changes: 5 additions & 3 deletions src/database/drop.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
DROP TABLE IF EXISTS field;
DROP TABLE IF EXISTS prompt;
DROP TABLE IF EXISTS target;
DROP TABLE IF EXISTS feedback_prompt_field_response;
DROP TABLE IF EXISTS feedback_prompt_response;
DROP TABLE IF EXISTS feedback_prompt_field;
DROP TABLE IF EXISTS feedback_prompt;
DROP TABLE IF EXISTS feedback_target;

40 changes: 35 additions & 5 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ macro_rules! database_configuration {
#[derive(Debug, Clone)]
pub enum DatabaseConfiguration {
$(
#[cfg(feature = "" $ident:lower)]
#[cfg(feature = $scheme)]
$ident($config),
)*
}
Expand All @@ -61,8 +61,8 @@ macro_rules! database_configuration {
#[inline(always)]
pub fn extract() -> crate::error::Result<Self> {
$(
#[cfg(feature = "" $ident:lower)]
if let Ok(config) = envy::prefixed(stringify!([<$ident:lower _>]).trim()).from_env::<$config>() {
#[cfg(feature = $scheme)]
if let Ok(config) = envy::prefixed(stringify!([<$ident:upper _>])).from_env::<$config>() {
return Ok(Self::$ident(config));
}
)*
Expand All @@ -77,12 +77,12 @@ macro_rules! database_configuration {

match self {
$(
#[cfg(feature = "" $ident:lower)]
#[cfg(feature = $scheme)]
Self::$ident(config) => {
let url = config.to_url($scheme);
connection.init($driver {}, url.as_str())?;

#[cfg(test)]
#[cfg(feature = "test")]
connection.exec(include_str!("drop.sql"), vec![]).await?;

// perform migrations
Expand Down Expand Up @@ -145,3 +145,33 @@ macro_rules! database_request {
}};
}

/// rbatis doesnt convert the LIMIT statements for postgres and mssql therefore we need a wrapper
/// REF: https://rbatis.github.io/rbatis.io/#/v4/?id=macros-select-page
#[macro_export]
macro_rules! impl_select_page_wrapper {
($table:path {}) => {
impl_select_page_wrapper!($table{select_page() => ""});
};
($table:path {$ident:ident ($($arg:ident: $ty:ty $(,)?)*) => $expr:expr}) => {
paste!{
impl_select_page!($table {$ident($($arg: $ty,)* limit_sql: &str) => $expr});

impl $table {
pub async fn [<$ident _wrapper>](executor: &dyn rbatis::executor::Executor, page_request: &dyn rbatis::IPageRequest, $($arg: $ty,)*) -> std::result::Result<rbatis::plugin::page::Page<$table>, rbatis::rbdc::Error> {

use std::ops::Deref;
let limit = page_request.page_size();
let offset = page_request.offset();

match $crate::DATABASE_CONFIG.deref() {
#[cfg(feature = "postgres")]
$crate::database::DatabaseConfiguration::Postgres(_) => Self::$ident(executor, page_request, $($arg,)* format!(" LIMIT {} OFFSET {} ", limit, offset).as_str()).await,
#[allow(unreachable_patterns)]
_ => Self::$ident(executor, page_request, $($arg,)* format!(" LIMIT {},{} ", limit, offset).as_str()).await
}
}
}
}

}
}
26 changes: 20 additions & 6 deletions src/database/postgres.sql
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
CREATE TABLE IF NOT EXISTS target (
CREATE TABLE IF NOT EXISTS feedback_target (
id VARCHAR(32) UNIQUE NOT NULL,
name VARCHAR(32) NOT NULL,
description VARCHAR(255),
updated_at TIMESTAMP,
created_at TIMESTAMP
);

CREATE TABLE IF NOT EXISTS prompt (
CREATE TABLE IF NOT EXISTS feedback_prompt (
id VARCHAR(32) UNIQUE NOT NULL,
title VARCHAR(32) NOT NULL,
target VARCHAR(32) REFERENCES target(id) NOT NULL,
target VARCHAR(32) REFERENCES feedback_target(id) NOT NULL,
active BOOLEAN NOT NULL,
updated_at TIMESTAMP,
created_at TIMESTAMP
);

CREATE TABLE IF NOT EXISTS field (
CREATE TABLE IF NOT EXISTS feedback_prompt_field (
id VARCHAR(32) UNIQUE NOT NULL,
title VARCHAR(255) NOT NULL,
prompt VARCHAR(32) REFERENCES prompt(id) NOT NULL,
prompt VARCHAR(32) REFERENCES feedback_prompt(id) NOT NULL,
type VARCHAR(32) NOT NULL,
options BPCHAR NOT NULL,
options JSON NOT NULL,
updated_at TIMESTAMP,
created_at TIMESTAMP
);

CREATE TABLE IF NOT EXISTS feedback_prompt_response (
id VARCHAR(32) UNIQUE NOT NULL,
prompt VARCHAR(32) REFERENCES feedback_prompt(id) NOT NULL,
created_at TIMESTAMP
);

CREATE TABLE IF NOT EXISTS feedback_prompt_field_response (
id VARCHAR(32) UNIQUE NOT NULL,
response VARCHAR(32) REFERENCES feedback_prompt_response(id) NOT NULL,
field VARCHAR(32) REFERENCES feedback_prompt_field(id) NOT NULL,
data JSON NOT NULL
);


86 changes: 82 additions & 4 deletions src/database/schema/feedback/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,27 @@
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use crate::prelude::*;
use rbatis::rbdc::{DateTime, JsonV};

use super::FeedbackPromptInputType;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, ToSchema)]
#[serde(tag = "type")]
#[serde(untagged)]
#[serde(rename_all = "lowercase")]
pub enum FeedbackPromptInputOptions {
Text(TextOptions),
Rating(RatingOptions)
Rating(RatingOptions),
}

// TODO: gen with macro
impl PartialEq<FeedbackPromptInputOptions> for FeedbackPromptInputType {
fn eq(&self, other: &FeedbackPromptInputOptions) -> bool {
match self {
Self::Text => matches!(other, FeedbackPromptInputOptions::Text(_)),
Self::Rating => matches!(other, FeedbackPromptInputOptions::Rating(_)),
}
}
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, TypedBuilder, ToSchema, Validate)]
Expand All @@ -33,14 +49,76 @@ pub struct TextOptions {
#[validate(length(max = 255))]
description: String,
#[validate(length(max = 255))]
placeholder: String
placeholder: String,
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, TypedBuilder, ToSchema, Validate)]
#[builder(field_defaults(setter(into)))]
pub struct RatingOptions {
#[validate(length(max = 255))]
description: String,
max: u8
max: u8,
}

#[derive(
Deserialize, Serialize, Clone, Derivative, Debug, Getters, MutGetters, TypedBuilder, ToSchema,
)]
#[derivative(PartialEq)]
#[get = "pub"]
#[get_mut = "pub"]
#[builder(field_defaults(setter(into)))]
pub struct FeedbackPromptResponse {
#[builder(default_code = r#"nanoid::nanoid!()"#)]
id: String,
prompt: String,
#[derivative(PartialEq = "ignore")]
#[builder(default)]
created_at: DateTime,
}

crud!(FeedbackPromptResponse {});
impl_select_page_wrapper!(FeedbackPromptResponse {select_page_by_prompt(prompt: &str) => "WHERE prompt = #{prompt}"});

#[derive(
Deserialize, Serialize, Clone, PartialEq, Debug, Getters, MutGetters, TypedBuilder, ToSchema,
)]
#[get = "pub"]
#[get_mut = "pub"]
#[builder(field_defaults(setter(into)))]
pub struct FeedbackPromptFieldResponse {
#[builder(default_code = r#"nanoid::nanoid!()"#)]
id: String,
response: String,
field: String,
#[schema(value_type = FeedbackPromptFieldData)]
data: JsonV<FeedbackPromptFieldData>,
}

crud!(FeedbackPromptFieldResponse {});

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)]
#[serde(untagged)]
pub enum FeedbackPromptFieldData {
Text(TextResponse),
Rating(RatingResponse),
}

// TODO: use macro
impl PartialEq<FeedbackPromptFieldData> for FeedbackPromptInputType {
fn eq(&self, other: &FeedbackPromptFieldData) -> bool {
match self {
Self::Text => matches!(other, FeedbackPromptFieldData::Text(_)),
Self::Rating => matches!(other, FeedbackPromptFieldData::Rating(_)),
}
}
}

#[derive(Deserialize, Serialize, Clone, Debug, ToSchema, PartialEq)]
pub struct TextResponse {
data: String,
}

#[derive(Deserialize, Serialize, Clone, Debug, ToSchema, PartialEq)]
pub struct RatingResponse {
data: u8,
}
21 changes: 13 additions & 8 deletions src/database/schema/feedback/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use rbatis::rbdc::DateTime;
use crate::prelude::*;
use rbatis::rbdc::{DateTime, JsonV};

use super::input::FeedbackPromptInputOptions;

Expand All @@ -31,14 +32,14 @@ use super::input::FeedbackPromptInputOptions;
Derivative,
Debug,
Getters,
MutGetters,
Setters,
TypedBuilder,
ToSchema,
Validate,
)]
#[derivative(PartialEq)]
#[get = "pub"]
#[get_mut = "pub"]
#[set = "pub"]
#[builder(field_defaults(setter(into)))]
pub struct FeedbackPrompt {
#[builder(default_code = r#"nanoid::nanoid!()"#)]
Expand All @@ -57,9 +58,11 @@ pub struct FeedbackPrompt {
}

crud!(FeedbackPrompt {});
impl_select_page!(FeedbackPrompt {select_page_by_target(target: &str) => "`WHERE target = #{target}`"});
impl_select!(FeedbackPrompt {select_by_id(id: &str) -> Option => "`WHERE id = #{id} LIMIT 1`"});
impl_select_page_wrapper!(FeedbackPrompt {select_page_by_target(target: &str) => "`WHERE target = #{target}`"});

#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum FeedbackPromptInputType {
Text,
Rating,
Expand All @@ -72,14 +75,14 @@ pub enum FeedbackPromptInputType {
Derivative,
Debug,
Getters,
MutGetters,
Setters,
TypedBuilder,
ToSchema,
Validate,
)]
#[derivative(PartialEq)]
#[get = "pub"]
#[get_mut = "pub"]
#[set = "pub"]
#[builder(field_defaults(setter(into)))]
pub struct FeedbackPromptField {
#[builder(default_code = r#"nanoid::nanoid!()"#)]
Expand All @@ -88,7 +91,8 @@ pub struct FeedbackPromptField {
title: String,
prompt: String,
r#type: FeedbackPromptInputType,
options: FeedbackPromptInputOptions,
#[schema(value_type = FeedbackPromptInputOptions)]
options: JsonV<FeedbackPromptInputOptions>,
#[builder(default)]
#[derivative(PartialEq = "ignore")]
updated_at: DateTime,
Expand All @@ -98,4 +102,5 @@ pub struct FeedbackPromptField {
}

crud!(FeedbackPromptField {});
impl_select_page!(FeedbackPromptField {select_page_by_prompt(prompt: &str) => "`WHERE prompt = #{prompt}`"});
impl_select!(FeedbackPromptField {select_by_id(id: &str) -> Option => "`WHERE id = #{id} LIMIT 1`"});
impl_select_page_wrapper!(FeedbackPromptField {select_page_by_prompt(prompt: &str) => "`WHERE prompt = #{prompt}`"});
Loading

0 comments on commit 863f4c5

Please sign in to comment.