Skip to content

Commit

Permalink
feat(backend): Implement get parameters
Browse files Browse the repository at this point in the history
Also implement type wrapper for SecStr in order to de- and serialize
it more easily.
Fixed some start up issues as well.

Refs: #2
  • Loading branch information
maikbasel committed Jul 24, 2024
1 parent 962a99b commit f69b68f
Show file tree
Hide file tree
Showing 20 changed files with 700 additions and 200 deletions.
374 changes: 341 additions & 33 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ aws-sdk-sts = "1.15.0"
log = "0.4.20"
aws-sdk-ssm = "1.30.0"
test-log = "0.2.16"
env_logger = "0.11.2"
chrono = { version = "0.4.38", features = ["serde"] }
devtools = "0.3.2"

[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/common.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod aws;
pub mod secure_string;
145 changes: 145 additions & 0 deletions src-tauri/src/common/secure_string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use std::fmt::Formatter;

use secstr::SecStr;
use serde::de::{Error, Visitor};
use serde::{Deserializer, Serializer};

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct SecureString(SecStr);

impl SecureString {
pub fn new(bytes: Vec<u8>) -> SecureString {
SecureString(SecStr::from(bytes))
}

pub fn as_str(&self) -> &str {
std::str::from_utf8(self.0.unsecure()).expect("should be serializable to be UTF-8 string")
}
}

impl From<&str> for SecureString {
fn from(s: &str) -> Self {
SecureString(SecStr::from(s))
}
}

impl From<String> for SecureString {
fn from(value: String) -> Self {
SecureString(SecStr::from(value))
}
}

impl From<Vec<u8>> for SecureString {
fn from(bytes: Vec<u8>) -> Self {
SecureString(SecStr::new(bytes))
}
}

impl serde::Serialize for SecureString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let secure_str = &self.0.unsecure();
let utf8_str = std::str::from_utf8(secure_str).expect("should serialize to utf8 string");

serializer.serialize_str(utf8_str)
}
}

struct SecureStringVisitor;

impl<'de> Visitor<'de> for SecureStringVisitor {
type Value = SecureString;

fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("struct SecureString")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(SecureString(SecStr::from(v)))
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: Error,
{
Ok(SecureString(SecStr::from(v)))
}
}

impl<'de> serde::Deserialize<'de> for SecureString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(SecureStringVisitor)
}
}

#[cfg(test)]
mod tests {
use spectral::prelude::*;

use super::*;

#[test]
fn should_serialize_secure_string_to_string() {
let secure_str = SecureString(SecStr::from("super_secret_password"));

let serialized = serde_json::to_string(&secure_str).unwrap();

assert_that!(serialized).is_equal_to("\"super_secret_password\"".to_string())
}

#[test]
fn should_serialize_empty_secure_string_to_string() {
let secure_str = SecureString(SecStr::from(""));

let serialized = serde_json::to_string(&secure_str).unwrap();

assert_that!(serialized).is_equal_to("\"\"".to_string());
}

#[test]
fn should_serialize_secure_string_with_special_chars_to_string() {
let secure_str = SecureString(SecStr::from("s3cr3t_$tr!ng"));

let serialized = serde_json::to_string(&secure_str).unwrap();

assert_eq!(serialized, "\"s3cr3t_$tr!ng\"");
}

#[test]
fn should_deserialize_str_to_secure_string() {
let serialized = "\"super_secret_password\"";

let deserialized: SecureString = serde_json::from_str(serialized).unwrap();

assert_eq!(
deserialized.0.unsecure(),
"super_secret_password".as_bytes()
);
}

#[test]
fn should_deserialize_empty_str_to_secure_string() {
let serialized = "\"\"";

let deserialized: SecureString = serde_json::from_str(serialized).unwrap();

assert_eq!(deserialized.0.unsecure(), "".as_bytes());
}

#[test]
fn should_deserialize_str_with_special_chars_to_secure_string() {
let serialized = "\"s3cr3t_$tr!ng\"";

let deserialized: SecureString = serde_json::from_str(serialized).unwrap();

assert_eq!(deserialized.0.unsecure(), "s3cr3t_$tr!ng".as_bytes());
}
}
2 changes: 1 addition & 1 deletion src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod common;
pub mod common;
pub mod credentials;
pub mod parameters;
pub mod profiles;
43 changes: 34 additions & 9 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,73 @@

use std::sync::Arc;

use tauri_plugin_log::LogTarget;

use backend::__cmd__create_profile;
use backend::__cmd__delete_profile;
use backend::__cmd__delete_profiles;
use backend::__cmd__edit_profile;
use backend::__cmd__get_parameters;
use backend::__cmd__get_profiles;
use backend::__cmd__validate_credentials;
use backend::credentials::application::tauri::credentials_handler::validate_credentials;
use backend::credentials::core::api::CredentialsDataAPI;
use backend::credentials::core::credentials_service::CredentialsService;
use backend::credentials::infrastructure::aws::sts::sts_adapter::STSAdapter;
use backend::parameters::application::tauri::parameters_handler::get_parameters;
use backend::parameters::core::api::ParameterDataAPI;
use backend::parameters::core::parameter_service::ParameterService;
use backend::parameters::infrastructure::aws::ssm::parameter_store_adapter::ParameterStoreAdapter;
use backend::profiles::application::tauri::profile_handler::{
create_profile, delete_profile, delete_profiles, edit_profile, get_profiles,
};
use backend::profiles::core::api::ProfileDataAPI;
use backend::profiles::core::profile_service::ProfileService;
use backend::profiles::infrastructure::aws::sdk_config::sdk_config_adapter::SdkConfigAdapter;

#[allow(unused_assignments)]
#[cfg(not(tarpaulin_include))]
fn main() {
env_logger::init();
let mut builder = tauri::Builder::default();

#[cfg(debug_assertions)]
{
let devtools = devtools::init();

builder = builder.plugin(devtools);
}

#[cfg(not(debug_assertions))]
{
use tauri_plugin_log::{Builder, Target, TargetKind};

let log_plugin = Builder::default()
.targets([
Target::new(TargetKind::Stdout),
Target::new(TargetKind::LogDir { file_name: None }),
Target::new(TargetKind::Webview),
])
.build();

builder = builder.plugin(log_plugin);
}

let profile_data_spi = SdkConfigAdapter;
let profile_data_api = ProfileService::new(Box::new(profile_data_spi));
let credentials_data_api = CredentialsService::new(Box::new(STSAdapter));
let parameter_data_spi = ParameterStoreAdapter;
let parameter_data_api = ParameterService::new(Box::new(parameter_data_spi));

tauri::Builder::default()
.plugin(
tauri_plugin_log::Builder::default()
.targets([LogTarget::LogDir, LogTarget::Stdout, LogTarget::Webview])
.build(),
)
.manage(Arc::new(profile_data_api) as Arc<dyn ProfileDataAPI>)
.manage(Arc::new(credentials_data_api) as Arc<dyn CredentialsDataAPI>)
.manage(Arc::new(parameter_data_api) as Arc<dyn ParameterDataAPI>)
.invoke_handler(tauri::generate_handler![
get_profiles,
create_profile,
edit_profile,
delete_profile,
validate_credentials,
delete_profiles
delete_profiles,
get_parameters,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/parameters.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod application;
pub mod application;
pub mod core;
pub mod infrastructure;
1 change: 1 addition & 0 deletions src-tauri/src/parameters/application.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod tauri;
1 change: 1 addition & 0 deletions src-tauri/src/parameters/application/tauri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod parameters_handler;
17 changes: 17 additions & 0 deletions src-tauri/src/parameters/application/tauri/parameters_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::sync::Arc;

use crate::parameters::core::api::ParameterDataAPI;
use crate::parameters::core::domain::Parameter;
use crate::parameters::core::error::ParameterDataError;

#[tauri::command]
#[cfg(not(tarpaulin_include))]
pub async fn get_parameters(
api: tauri::State<'_, Arc<dyn ParameterDataAPI>>,
profile_name: String,
page_size: u32,
) -> Result<Vec<Parameter>, ParameterDataError> {
let result = api.get_parameters(profile_name.as_str(), page_size).await;

result.map_err(ParameterDataError::from)
}
2 changes: 1 addition & 1 deletion src-tauri/src/parameters/core.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod api;
pub mod domain;
pub mod error;
mod parameter_service;
pub mod parameter_service;
pub mod spi;
15 changes: 9 additions & 6 deletions src-tauri/src/parameters/core/domain.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
use aws_sdk_sts::primitives::DateTime;
use secstr::SecStr;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq)]
use crate::common::secure_string::SecureString;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ParameterValue {
String(String),
StringList(Vec<String>),
SecureString(SecStr),
SecureString(SecureString),
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Parameter {
pub name: String,
pub value: ParameterValue,
pub version: i64,
pub last_modified_date: Option<DateTime>,
// #[serde(with = "ts_milliseconds_option")]
pub last_modified_date: Option<DateTime<Utc>>,
pub identifier: Option<String>,
}
23 changes: 22 additions & 1 deletion src-tauri/src/parameters/core/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::{Display, Formatter};

use error_stack::Context;
use error_stack::{Context, Report};
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};
use serde_json::json;
Expand All @@ -16,6 +16,13 @@ pub enum ParameterDataError {

impl Context for ParameterDataError {}

impl From<Report<ParameterDataError>> for ParameterDataError {
fn from(value: Report<ParameterDataError>) -> Self {
let context = value.current_context();
context.clone()
}
}

impl Display for ParameterDataError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -62,10 +69,24 @@ impl Serialize for ParameterDataError {

#[cfg(test)]
mod tests {
use error_stack::Report;
use serde_json::json;

use crate::profiles::core::error::ProfileDataError;

use super::*;

#[test]
fn should_return_parameter_data_load_error_when_calling_from_on_report_with_context_parameter_data_load_error(
) {
let error = ParameterDataError::ParameterDataLoadError;
let report = Report::new(error.clone());

let result: ParameterDataError = report.into();

assert_eq!(error, result);
}

#[test]
fn should_serialize_parameter_metadata_load_error() {
let error = ParameterDataError::ParameterMetaDataLoadError;
Expand Down
Loading

0 comments on commit f69b68f

Please sign in to comment.