Skip to content

Commit

Permalink
TC-1672 Dashboard - User Preferences back end (trustification#1695)
Browse files Browse the repository at this point in the history
* TC-1672 sqllite db init

* chore: user reset endpoint

* suggest changes

Signed-off-by: carlosthe19916 <[email protected]>

---------

Signed-off-by: carlosthe19916 <[email protected]>
Co-authored-by: carlosthe19916 <[email protected]>
  • Loading branch information
bxf12315 and carlosthe19916 authored Sep 13, 2024
1 parent aee88ab commit ef7e39c
Show file tree
Hide file tree
Showing 18 changed files with 396 additions and 13 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

27 changes: 27 additions & 0 deletions auth/src/client/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
use actix_http::body::BoxBody;
use actix_http::StatusCode;
use actix_web::http::header::ContentType;
use actix_web::{HttpResponse, ResponseError};
use trustification_common::error::ErrorInformation;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
OpenId(#[from] openid::error::Error),
}

impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
match self {
Error::OpenId(_) => StatusCode::UNAUTHORIZED,
}
}

fn error_response(&self) -> HttpResponse<BoxBody> {
let mut res = HttpResponse::build(self.status_code());
res.insert_header(ContentType::json());

match self {
Error::OpenId(openid) => res.json(ErrorInformation {
error: format!("{}", self.status_code()),
message: format!("{}", openid),
details: openid.to_string(),
}),
}
}
}
1 change: 1 addition & 0 deletions deploy/compose/.env
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ CHROMEDRIVER_IMAGE=docker.io/selenium/standalone-chrome:117.0
JAEGER_IMAGE=docker.io/jaegertracing/all-in-one:latest
JOB_REPORT_ENABLED=true
REPORT_PATH=/tmp/share/reports
USER_PREFERENCES_DB_PATH=/tmp/share/db/
4 changes: 3 additions & 1 deletion deploy/compose/compose-trustification.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ services:
sleep 5
done
echo keycloak is up
/trust spog api --devmode --bombastic-url http://bombastic-api:$BOMBASTIC_API_PORT --vexination-url http://vexination-api:$VEXINATION_API_PORT --guac http://guac-graphql:$GUAC_API_PORT/query --v11y-url http://v11y-api:$V11Y_API_PORT
/trust spog api --devmode --db-storage-base $USER_PREFERENCES_DB_PATH --bombastic-url http://bombastic-api:$BOMBASTIC_API_PORT --vexination-url http://vexination-api:$VEXINATION_API_PORT --guac http://guac-graphql:$GUAC_API_PORT/query --v11y-url http://v11y-api:$V11Y_API_PORT
entrypoint: /usr/bin/bash
restart: on-failure
healthcheck:
Expand All @@ -106,6 +106,8 @@ services:
INFRASTRUCTURE_ENABLED: "true"
OPENID_CONFIGURATION: "http://keycloak:8080/realms/chicken/.well-known/openid-configuration"
HTTP_SERVER_BIND_ADDR: "::"
volumes:
- $USER_PREFERENCES_DB_PATH:/tmp/share/db/

spog-ui:
command: /nginx.sh
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{/*
Volume mounts for the user preferences configuration.
Arguments (dict):
* root - .
* module - module object
*/}}
{{- define "trustification.preferences.db.volumeMount" }}
- name: user-preferences-db-path
mountPath: /data/db/
{{- end }}


{{/*
Volume for the user preferences data.
*/}}
{{- define "trustification.preferences.db.volume" }}
- name: user-preferences-db-path
persistentVolumeClaim:
claimName: user-preferences-db-path
{{- end }}

{{/*
db path
*/}}
{{- define "trustification.preferences.db.path" -}}
/data/db/
{{- end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{{- if .Values.modules.spogApi.enabled }}
{{- $mod := dict "root" . "name" "user-preferences-db-path" "component" "spog" "module" .Values.modules.spogApi -}}
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ include "trustification.common.name" $mod }}
labels:
{{- include "trustification.common.labels" $mod | nindent 4 }}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ $mod.module.storageSize | default "10Mi" | quote }}
{{end}}
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ spec:
{{- include "trustification.application.container" $mod | nindent 10 }}
{{- include "trustification.application.infrastructure.probes" $mod | nindent 10 }}

command: ["/trust"]
command: [ "/trust" ]
args:
- "spog"
- "api"
- "-p"
- "8080"
- "--db-storage-base"
- "{{ include "trustification.preferences.db.path" $mod}}"
- "--bombastic-url"
- "{{ include "trustification.tls.http.protocol" $mod }}://bombastic-api.{{ .Release.Namespace }}.svc.cluster.local"
- "--vexination-url"
Expand Down Expand Up @@ -98,7 +100,7 @@ spec:
volumeMounts:
{{- include "trustification.application.httpServerVolumesMounts" $mod | nindent 12 }}
{{- include "trustification.authenticator.volumeMount" $mod | nindent 12 }}

{{- include "trustification.preferences.db.volumeMount" $mod | nindent 12 }}
{{- if $mod.module.uiConfiguration }}
- name: config-ui
mountPath: /etc/config/spog-ui.yaml
Expand All @@ -118,5 +120,6 @@ spec:
{{- end }}

{{- include "trustification.application.extraVolumes" $mod | nindent 8 }}
{{- include "trustification.preferences.db.volume" $mod | nindent 8 }}

{{ end }}
3 changes: 3 additions & 0 deletions deploy/k8s/charts/trustification/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@
{
"type": "object",
"properties": {
"storageSize": {
"type": "string"
},
"crdaUrl": {
"type": "string",
"format": "uri"
Expand Down
2 changes: 2 additions & 0 deletions deploy/k8s/charts/trustification/values.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ properties:
- $ref: "#/definitions/HttpApplication"
- type: object
properties:
storageSize:
type: string
crdaUrl:
type: string
format: uri
Expand Down
1 change: 1 addition & 0 deletions deploy/k8s/charts/trustification/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ modules:
tracing: {}
metrics: {}
rust: {}
storageSize: "10Mi"
crdaUrl: https://rhda.rhcloud.com/api/v4/analysis

spogUi:
Expand Down
1 change: 1 addition & 0 deletions integration-tests/src/spog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,6 @@ fn spog_api(
swagger_ui_oidc: testing_swagger_ui_oidc(),
analytics: Default::default(),
http: Default::default(),
db_storage_base: None,
}
}
1 change: 1 addition & 0 deletions spog/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ trustification-infrastructure = { path = "../../infrastructure" }
v11y-client = { path = "../../v11y/client" }
trustification-version = { path = "../../version", features = ["actix-web"] }
time = "0.3.31"
sqlx = { version = "0.7.0", features = ["runtime-tokio", "sqlite", "chrono"] }

[build-dependencies]
trustification-version = { path = "../../version", features = ["build"] }
2 changes: 2 additions & 0 deletions spog/api/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use bytes::Bytes;
use http::StatusCode;
use tracing::instrument;

use crate::db::Db;
use trustification_api::search::SearchOptions;
use trustification_api::Apply;
use trustification_auth::client::{TokenInjector, TokenProvider};
Expand All @@ -18,6 +19,7 @@ pub struct AppState {
pub bombastic: reqwest::Url,
pub vexination: reqwest::Url,
pub exhort: reqwest::Url,
pub db_storage: Db,
}

impl AppState {
Expand Down
194 changes: 194 additions & 0 deletions spog/api/src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use actix_web::body::BoxBody;
use actix_web::http::header::ContentType;
use actix_web::{HttpResponse, ResponseError};
use http::StatusCode;
use spog_model::dashboard::UserPreferences;
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::{Row, SqlitePool};
use std::path::Path;
use std::str::FromStr;
use trustification_common::error::ErrorInformation;

#[allow(dead_code)]
static DB_FILE_NAME: &str = "preferences.db";

#[allow(dead_code)]
pub struct Db {
pool: SqlitePool,
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("JSON parse error: {0}")]
Json(#[from] serde_json::Error),
#[error("data base error: {0}")]
Db(#[from] sqlx::Error),
}

impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
match self {
Error::Json(_) => StatusCode::INTERNAL_SERVER_ERROR,
Error::Db(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}

fn error_response(&self) -> HttpResponse<BoxBody> {
let mut res = HttpResponse::build(self.status_code());
res.insert_header(ContentType::json());

match self {
Error::Json(json) => res.json(ErrorInformation {
error: format!("{}", self.status_code()),
message: format!("{}", json),
details: json.to_string(),
}),
Error::Db(db) => res.json(ErrorInformation {
error: format!("{}", self.status_code()),
message: format!("{}", db),
details: db.to_string(),
}),
}
}
}

impl Db {
#[allow(dead_code)]
pub async fn new(base: impl AsRef<Path>) -> Result<Self, Error> {
let db = Self {
pool: SqlitePool::connect_with(if cfg!(test) {
SqliteConnectOptions::from_str(":memory:")?
} else {
SqliteConnectOptions::default()
.filename(base.as_ref().join(DB_FILE_NAME))
.create_if_missing(true)
})
.await?,
};
db.initialize().await?;
Ok(db)
}

#[allow(dead_code)]
async fn initialize(&self) -> Result<(), Error> {
self.create_user_preferences_table().await?;
Ok(())
}

#[allow(dead_code)]
pub async fn create_user_preferences_table(&self) -> Result<(), Error> {
sqlx::query(
r#"CREATE TABLE IF NOT EXISTS preferences (
user_id TEXT,
preference TEXT
)"#,
)
.execute(&self.pool)
.await?;

sqlx::query(
r#"
create unique index if not exists user_id_idx on preferences ( user_id ) ;
"#,
)
.execute(&self.pool)
.await?;

Ok(())
}
#[allow(dead_code)]
pub async fn update_user_preferences(&self, preferences: UserPreferences) -> Result<(), Error> {
let content = preferences.preferences.unwrap_or_default();

sqlx::query(
r#"
INSERT OR REPLACE INTO preferences ( user_id, preference)
VALUES ($1, $2);
"#,
)
.bind(preferences.user_id)
.bind(serde_json::to_string(&content).unwrap_or_default())
.execute(&self.pool)
.await?;

Ok(())
}
#[allow(dead_code)]
pub async fn select_preferences_by_user_id(&self, user_id: String) -> Result<UserPreferences, Error> {
let result = sqlx::query(
r#"
select user_id, preference from preferences where user_id = $1;
"#,
)
.bind(user_id)
.fetch_all(&self.pool)
.await?;

if result.is_empty() {
Ok(UserPreferences::default())
} else {
let p = UserPreferences {
user_id: result[0].get("user_id"),
preferences: serde_json::from_str(result[0].get("preference"))?,
};
Ok(p)
}
}
}
#[cfg(test)]
mod test {
use crate::db::Db;
use spog_model::dashboard::{Preferences, UserPreferences};
#[actix_web::test]
async fn update_user_preferences() -> Result<(), anyhow::Error> {
let pre_preferences = Preferences {
sbom1: Some("11".to_string()),
sbom2: Some("21".to_string()),
sbom3: Some("31".to_string()),
sbom4: Some("41".to_string()),
};

let post_preferences = Preferences {
sbom1: Some("1".to_string()),
sbom2: Some("2".to_string()),
sbom3: Some("3".to_string()),
sbom4: Some("4".to_string()),
};

let db = Db::new(".").await?;
db.update_user_preferences(UserPreferences {
user_id: "xiabai".to_string(),
preferences: Some(pre_preferences.clone()),
})
.await?;
db.update_user_preferences(UserPreferences {
user_id: "user1".to_string(),
preferences: Some(pre_preferences.clone()),
})
.await?;

let result = db.select_preferences_by_user_id("user1".to_string()).await?;
assert_eq!("user1", result.user_id);
assert_eq!(
"11".to_string(),
result.preferences.unwrap_or_default().sbom1.unwrap_or_default()
);

db.update_user_preferences(UserPreferences {
user_id: "user1".to_string(),
preferences: Some(post_preferences.clone()),
})
.await?;

let result = db.select_preferences_by_user_id("xiabai".to_string()).await?;
assert_eq!("xiabai", result.user_id);

let result = db.select_preferences_by_user_id("user1".to_string()).await?;
assert_eq!("user1", result.user_id);
assert_eq!(
"1".to_string(),
result.preferences.unwrap_or_default().sbom1.unwrap_or_default()
);
Ok(())
}
}
Loading

0 comments on commit ef7e39c

Please sign in to comment.