Skip to content

Commit

Permalink
feat(platform): make subscription management and feature overview URL…
Browse files Browse the repository at this point in the history
…s configurable
  • Loading branch information
azasypkin committed Mar 2, 2024
1 parent db19b34 commit 70faae9
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 20 deletions.
5 changes: 4 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ mod js_runtime_config;
mod scheduler_jobs_config;
mod smtp_catch_all_config;
mod smtp_config;
mod subscriptions_config;

use crate::server::WebhookUrlType;
use url::Url;

pub use self::{
components_config::ComponentsConfig, js_runtime_config::JsRuntimeConfig,
scheduler_jobs_config::SchedulerJobsConfig, smtp_catch_all_config::SmtpCatchAllConfig,
smtp_config::SmtpConfig,
smtp_config::SmtpConfig, subscriptions_config::SubscriptionsConfig,
};

/// Secutils.dev user agent name used for all HTTP requests.
Expand All @@ -36,6 +37,8 @@ pub struct Config {
pub jobs: SchedulerJobsConfig,
/// Configuration for the JS runtime.
pub js_runtime: JsRuntimeConfig,
/// Configuration related to the Secutils.dev subscriptions.
pub subscriptions: SubscriptionsConfig,
}

impl AsRef<Config> for Config {
Expand Down
10 changes: 10 additions & 0 deletions src/config/subscriptions_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use url::Url;

/// Configuration related to the Secutils.dev subscriptions.
#[derive(Clone, Debug)]
pub struct SubscriptionsConfig {
/// The URL to access the subscription management page.
pub manage_url: Option<Url>,
/// The URL to access the feature overview page.
pub feature_overview_url: Option<Url>,
}
34 changes: 32 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod utils;
use crate::{
config::{
ComponentsConfig, Config, JsRuntimeConfig, SchedulerJobsConfig, SmtpCatchAllConfig,
SmtpConfig,
SmtpConfig, SubscriptionsConfig,
},
server::WebhookUrlType,
};
Expand Down Expand Up @@ -140,6 +140,18 @@ fn process_command(version: &str, matches: ArgMatches) -> Result<(), anyhow::Err
anyhow!("<JS_RUNTIME_MAX_USER_SCRIPT_EXECUTION_TIME> argument is not provided.")
})?,
},
subscriptions: SubscriptionsConfig {
manage_url: matches
.get_one::<String>("SUBSCRIPTIONS_MANAGE_URL")
.map(|value| Url::parse(value.as_str()))
.transpose()
.with_context(|| "Cannot parse subscription management URL.")?,
feature_overview_url: matches
.get_one::<String>("SUBSCRIPTIONS_FEATURE_OVERVIEW_URL")
.map(|value| Url::parse(value.as_str()))
.transpose()
.with_context(|| "Cannot parse subscription feature overview URL.")?,
},
};

let session_key = matches
Expand Down Expand Up @@ -311,6 +323,20 @@ fn main() -> Result<(), anyhow::Error> {
.default_value("30")
.help("Defines the maximum duration for a single JS script execution in seconds."),
)
.arg(
Arg::new("SUBSCRIPTIONS_MANAGE_URL")
.long("subscriptions-manage-url")
.global(true)
.env("SECUTILS_SUBSCRIPTIONS_MANAGE_URL")
.help("Defines the URL to access the subscription management page."),
)
.arg(
Arg::new("SUBSCRIPTIONS_FEATURE_OVERVIEW_URL")
.long("subscriptions-feature-overview-url")
.global(true)
.env("SECUTILS_SUBSCRIPTIONS_FEATURE_OVERVIEW_URL")
.help("Defines the URL to access the feature overview page."),
)
.get_matches();

process_command(version, matches)
Expand All @@ -320,7 +346,7 @@ fn main() -> Result<(), anyhow::Error> {
mod tests {
use crate::{
api::Api,
config::{ComponentsConfig, Config, SchedulerJobsConfig, SmtpConfig},
config::{ComponentsConfig, Config, SchedulerJobsConfig, SmtpConfig, SubscriptionsConfig},
database::Database,
network::{DnsResolver, Network},
search::SearchItem,
Expand Down Expand Up @@ -536,6 +562,10 @@ mod tests {
max_heap_size_bytes: 10485760,
max_user_script_execution_time: Duration::from_secs(30),
},
subscriptions: SubscriptionsConfig {
manage_url: Some(Url::parse("http:https://localhost:1234/subscription")?),
feature_overview_url: Some(Url::parse("http:https://localhost:1234/features")?),
},
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use std::sync::Arc;
pub use self::app_state::tests;

pub use app_state::AppState;
pub use ui_state::{Status, StatusLevel, UiState, WebhookUrlType};
pub use ui_state::{Status, StatusLevel, SubscriptionState, UiState, WebhookUrlType};

#[tokio::main]
pub async fn run(
Expand Down
8 changes: 6 additions & 2 deletions src/server/handlers/ui_state_get.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
error::Error as SecutilsError,
server::{AppState, UiState},
server::{AppState, SubscriptionState, UiState},
users::{ClientUserShare, PublicUserDataNamespace, User, UserShare},
};
use actix_web::{web, HttpResponse};
Expand Down Expand Up @@ -42,7 +42,11 @@ pub async fn ui_state_get(
Ok(HttpResponse::Ok().json(UiState {
status: status.deref(),
user,
features,
subscription: SubscriptionState {
features,
manage_url: state.config.subscriptions.manage_url.as_ref(),
feature_overview_url: state.config.subscriptions.feature_overview_url.as_ref(),
},
user_share: user_share.map(ClientUserShare::from),
settings,
utils,
Expand Down
53 changes: 39 additions & 14 deletions src/server/ui_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ mod status;
mod status_level;
mod webhook_url_type;

pub use self::{status::Status, status_level::StatusLevel, webhook_url_type::WebhookUrlType};
mod subscription_state;

pub use self::{
status::Status, status_level::StatusLevel, subscription_state::SubscriptionState,
webhook_url_type::WebhookUrlType,
};
use crate::{
users::{ClientUserShare, SubscriptionFeatures, User, UserSettings},
users::{ClientUserShare, User, UserSettings},
utils::Util,
};
use serde::Serialize;
Expand All @@ -15,8 +20,8 @@ pub struct UiState<'a> {
pub status: &'a Status,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<User>,
#[serde(skip_serializing_if = "Option::is_none")]
pub features: Option<SubscriptionFeatures>,
#[serde(skip_serializing_if = "default")]
pub subscription: SubscriptionState<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_share: Option<ClientUserShare>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -25,31 +30,47 @@ pub struct UiState<'a> {
pub webhook_url_type: WebhookUrlType,
}

fn default<T: Default + PartialEq>(t: &T) -> bool {
*t == Default::default()
}

#[cfg(test)]
mod tests {
use std::collections::BTreeMap;

use insta::assert_json_snapshot;
use serde_json::json;
use time::OffsetDateTime;
use url::Url;
use uuid::uuid;

use crate::{
server::{Status, StatusLevel, UiState, WebhookUrlType},
server::{
ui_state::subscription_state::SubscriptionState, Status, StatusLevel, UiState,
WebhookUrlType,
},
tests::{mock_config, mock_user},
users::{ClientUserShare, SharedResource, UserId, UserShare, UserShareId},
utils::Util,
};
use insta::assert_json_snapshot;
use serde_json::json;
use std::collections::BTreeMap;
use time::OffsetDateTime;
use uuid::uuid;

#[test]
fn serialization() -> anyhow::Result<()> {
let user = mock_user()?;
let features = user.subscription.get_features(&mock_config()?);
let manage_url = Url::parse("http:https://localhost:1234/subscription")?;
let feature_overview_url = Url::parse("http:https://localhost:1234/features")?;
let ui_state = UiState {
status: &Status {
version: "1.0.0-alpha.4".to_string(),
level: StatusLevel::Available,
},
user: Some(user),
features: Some(features),
subscription: SubscriptionState {
features: Some(features),
manage_url: Some(&manage_url),
feature_overview_url: Some(&feature_overview_url),
},
user_share: Some(ClientUserShare::from(UserShare {
id: UserShareId::from(uuid!("00000000-0000-0000-0000-000000000001")),
user_id: UserId::default(),
Expand Down Expand Up @@ -92,8 +113,12 @@ mod tests {
"startedAt": 1262340001
}
},
"features": {
"admin": true
"subscription": {
"features": {
"admin": true
},
"manageUrl": "http:https://localhost:1234/subscription",
"featureOverviewUrl": "http:https://localhost:1234/features"
},
"userShare": {
"id": "00000000-0000-0000-0000-000000000001",
Expand Down Expand Up @@ -127,7 +152,7 @@ mod tests {
level: StatusLevel::Available,
},
user: None,
features: None,
subscription: Default::default(),
user_share: None,
settings: None,
utils: vec![],
Expand Down
54 changes: 54 additions & 0 deletions src/server/ui_state/subscription_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::users::SubscriptionFeatures;
use serde_derive::Serialize;
use url::Url;

/// Defines subscription related properties returned as a part of the UI state.
#[derive(Clone, Serialize, Default, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SubscriptionState<'u> {
/// The subscription-dependent features available to the user.
#[serde(skip_serializing_if = "Option::is_none")]
pub features: Option<SubscriptionFeatures>,
/// The URL to the subscription management page.
#[serde(skip_serializing_if = "Option::is_none")]
pub manage_url: Option<&'u Url>,
/// The URL to the subscription overview page.
#[serde(skip_serializing_if = "Option::is_none")]
pub feature_overview_url: Option<&'u Url>,
}

#[cfg(test)]
mod tests {
use crate::{
server::SubscriptionState,
tests::{mock_config, mock_user},
};
use insta::assert_json_snapshot;
use url::Url;

#[test]
fn serialization() -> anyhow::Result<()> {
assert_json_snapshot!(SubscriptionState::default(), @"{}");

let user = mock_user()?;
let features = user.subscription.get_features(&mock_config()?);
let manage_url = Url::parse("http:https://localhost:1234/subscription")?;
let feature_overview_url = Url::parse("http:https://localhost:1234/features")?;

assert_json_snapshot!(SubscriptionState {
features: Some(features),
manage_url: Some(&manage_url),
feature_overview_url: Some(&feature_overview_url),
}, @r###"
{
"features": {
"admin": true
},
"manageUrl": "http:https://localhost:1234/subscription",
"featureOverviewUrl": "http:https://localhost:1234/features"
}
"###);

Ok(())
}
}

0 comments on commit 70faae9

Please sign in to comment.