Skip to content

Commit

Permalink
feat(rust): integrate space's subscription data in command
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianbenavides committed Jul 12, 2024
1 parent 6ff0623 commit 869b9f4
Show file tree
Hide file tree
Showing 22 changed files with 468 additions and 281 deletions.
20 changes: 18 additions & 2 deletions implementations/rust/ockam/ockam_api/src/cli_state/spaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ockam_core::Error;

use crate::cli_state::CliState;
use crate::cloud::space::Space;
use crate::cloud::subscription::Subscription;

use super::Result;

Expand All @@ -13,12 +14,14 @@ impl CliState {
space_id: &str,
space_name: &str,
users: Vec<&str>,
subscription: Option<&Subscription>,
) -> Result<Space> {
let repository = self.spaces_repository();
let space = Space {
id: space_id.to_string(),
name: space_name.to_string(),
users: users.iter().map(|u| u.to_string()).collect(),
subscription: subscription.cloned(),
};

repository.store_space(&space).await?;
Expand Down Expand Up @@ -96,13 +99,26 @@ mod test {

// the first created space becomes the default
let space1 = cli
.store_space("1", "name1", vec!["[email protected]", "[email protected]"])
.store_space(
"1",
"name1",
vec!["[email protected]", "[email protected]"],
Some(&Subscription {
name: "name1".to_string(),
is_free_trial: false,
marketplace: None,
start_date: None,
end_date: None,
}),
)
.await?;
let result = cli.get_default_space().await?;
assert_eq!(result, space1);

// the store method can be used to update a space
let updated_space1 = cli.store_space("1", "name1", vec!["[email protected]"]).await?;
let updated_space1 = cli
.store_space("1", "name1", vec!["[email protected]"], None)
.await?;
let result = cli.get_default_space().await?;
assert_eq!(result, updated_space1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use sqlx::*;

use ockam_core::async_trait;
use ockam_core::Result;
use ockam_node::database::{Boolean, FromSqlxError, SqlxDatabase, ToVoid};
use ockam_node::database::{Boolean, FromSqlxError, Nullable, SqlxDatabase, ToVoid};

use crate::cloud::space::Space;

Expand Down Expand Up @@ -42,14 +42,20 @@ impl SpacesRepository for SpacesSqlxDatabase {

let query2 = query(
r#"
INSERT INTO space (space_id, space_name, is_default)
VALUES ($1, $2, $3)
INSERT INTO space (space_id, space_name, is_default, subscription)
VALUES ($1, $2, $3, $4)
ON CONFLICT (space_id)
DO UPDATE SET space_name = $2, is_default = $3"#,
DO UPDATE SET space_name = $2, is_default = $3, subscription = $4"#,
)
.bind(&space.id)
.bind(&space.name)
.bind(is_already_default);
.bind(is_already_default)
.bind(
space
.subscription
.as_ref()
.and_then(|s| serde_json::to_string(s).ok()),
);
query2.execute(&mut *transaction).await.void()?;

// remove any existing users related to that space if any
Expand Down Expand Up @@ -91,7 +97,8 @@ impl SpacesRepository for SpacesSqlxDatabase {
let mut transaction = self.database.begin().await.into_core()?;

let query1 =
query_as("SELECT space_id, space_name FROM space WHERE space_name = $1").bind(name);
query_as("SELECT space_id, space_name, subscription FROM space WHERE space_name = $1")
.bind(name);
let row: Option<SpaceRow> = query1.fetch_optional(&mut *transaction).await.into_core()?;
let space = match row.map(|r| r.space()) {
Some(mut space) => {
Expand All @@ -113,7 +120,7 @@ impl SpacesRepository for SpacesSqlxDatabase {
async fn get_spaces(&self) -> Result<Vec<Space>> {
let mut transaction = self.database.begin().await.into_core()?;

let query = query_as("SELECT space_id, space_name FROM space");
let query = query_as("SELECT space_id, space_name, subscription FROM space");
let row: Vec<SpaceRow> = query.fetch_all(&mut *transaction).await.into_core()?;

let mut spaces = vec![];
Expand Down Expand Up @@ -180,6 +187,7 @@ impl SpacesRepository for SpacesSqlxDatabase {
struct SpaceRow {
space_id: String,
space_name: String,
subscription: Nullable<String>,
}

impl SpaceRow {
Expand All @@ -192,6 +200,11 @@ impl SpaceRow {
id: self.space_id.clone(),
name: self.space_name.clone(),
users: user_emails,
subscription: self
.subscription
.to_option()
.as_ref()
.and_then(|s| serde_json::from_str(s).ok()),
}
}
}
Expand All @@ -207,6 +220,7 @@ struct UserSpaceRow {
#[cfg(test)]
mod test {
use super::*;
use crate::cloud::subscription::Subscription;
use ockam_node::database::with_dbs;
use std::sync::Arc;

Expand All @@ -220,6 +234,7 @@ mod test {
id: "1".to_string(),
name: "name1".to_string(),
users: vec!["[email protected]".to_string(), "[email protected]".to_string()],
subscription: None,
};
let mut space2 = Space {
id: "2".to_string(),
Expand All @@ -229,6 +244,13 @@ mod test {
"[email protected]".to_string(),
"[email protected]".to_string(),
],
subscription: Some(Subscription {
name: "premium".to_string(),
is_free_trial: false,
marketplace: Some("aws".to_string()),
start_date: Some(chrono::Utc::now().to_string()),
end_date: Some((chrono::Utc::now() + chrono::Duration::days(30)).to_string()),
}),
};

repository.store_space(&space1).await?;
Expand Down
21 changes: 4 additions & 17 deletions implementations/rust/ockam/ockam_api/src/cloud/share/invitation.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use crate::address::extract_address_value;
use crate::cli_state::EnrollmentTicket;
use crate::cloud::email_address::EmailAddress;
use crate::date::is_expired;
use crate::error::ApiError;
use crate::output::Output;
use minicbor::{CborLen, Decode, Encode};
use ockam::identity::Identifier;
use serde::{Deserialize, Serialize};
use std::{fmt::Display, str::FromStr};
use time::format_description::well_known::iso8601::Iso8601;
use time::OffsetDateTime;

#[derive(Clone, Debug, Eq, PartialEq, Decode, Encode, CborLen, Deserialize, Serialize)]
#[cbor(index_only)]
Expand Down Expand Up @@ -149,20 +148,6 @@ impl Output for SentInvitation {
}
}

/// Check if a string that represents an Iso8601 date is expired, using the `time` crate
fn is_expired(date: &str) -> ockam_core::Result<bool> {
// Add the Z timezone to the date, as the `time` crate requires it
let date = if date.ends_with('Z') {
date.to_string()
} else {
format!("{}Z", date)
};
let now = OffsetDateTime::now_utc();
let date = OffsetDateTime::parse(&date, &Iso8601::DEFAULT)
.map_err(|e| ApiError::core(e.to_string()))?;
Ok(date < now)
}

#[derive(Clone, Debug, Encode, Decode, CborLen, Deserialize, Serialize, PartialEq)]
#[cbor(map)]
#[rustfmt::skip]
Expand Down Expand Up @@ -192,7 +177,9 @@ impl ServiceAccessDetails {

#[cfg(test)]
mod test {
use super::*;
use crate::date::is_expired;
use time::format_description::well_known::Iso8601;
use time::OffsetDateTime;

#[test]
fn test_is_expired() {
Expand Down
66 changes: 38 additions & 28 deletions implementations/rust/ockam/ockam_api/src/cloud/space.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use colorful::Colorful;
use miette::IntoDiagnostic;
use minicbor::{CborLen, Decode, Encode};
use serde::Serialize;
use std::fmt::Write;
use std::fmt::{Display, Formatter};

use ockam_core::api::Request;
use ockam_core::async_trait;
use ockam_node::Context;

use crate::cloud::project::{Project, ProjectsOrchestratorApi};
use crate::cloud::subscription::Subscription;
use crate::cloud::{ControllerClient, HasSecureClient};
use crate::colors::OckamColor;
use crate::colors::color_primary;
use crate::nodes::InMemoryNode;
use crate::output::{comma_separated, Output};
use crate::terminal::fmt;

const TARGET: &str = "ockam_api::cloud::space";

Expand All @@ -23,6 +24,7 @@ pub struct Space {
#[n(1)] pub id: String,
#[n(2)] pub name: String,
#[n(3)] pub users: Vec<String>,
#[n(4)] pub subscription: Option<Subscription>,
}

impl Space {
Expand All @@ -33,37 +35,41 @@ impl Space {
pub fn space_name(&self) -> String {
self.name.clone()
}
}

impl Output for Space {
fn item(&self) -> crate::Result<String> {
let mut w = String::new();
write!(w, "Space")?;
write!(w, "\n Id: {}", self.id)?;
write!(w, "\n Name: {}", self.name)?;
write!(w, "\n Users: {}", comma_separated(&self.users))?;
Ok(w)
pub fn has_subscription(&self) -> bool {
self.subscription.is_some()
}

fn as_list_item(&self) -> crate::Result<String> {
let mut output = String::new();
writeln!(
output,
"Space {}",
self.name
.to_string()
.color(OckamColor::PrimaryResource.color())
)?;
pub fn is_in_free_trial_subscription(&self) -> bool {
self.subscription.is_none()
|| self
.subscription
.as_ref()
.map(|s| s.is_free_trial)
.unwrap_or_default()
}
}

impl Display for Space {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", color_primary(&self.name))?;
writeln!(f, "{}Id: {}", fmt::INDENTATION, color_primary(&self.id))?;
writeln!(
output,
"Id {}",
self.id
.to_string()
.color(OckamColor::PrimaryResource.color())
f,
"{}Users: {}",
fmt::INDENTATION,
comma_separated(&self.users)
)?;
write!(output, "{}", comma_separated(&self.users))?;
if let Some(subscription) = &self.subscription {
write!(f, "{}", subscription.iter_output()?.indent())?;
}
Ok(())
}
}

Ok(output)
impl Output for Space {
fn item(&self) -> crate::Result<String> {
Ok(self.padded_display()?)
}
}

Expand Down Expand Up @@ -118,6 +124,7 @@ impl Spaces for InMemoryNode {
&space.id,
&space.name,
space.users.iter().map(|u| u.as_ref()).collect(),
space.subscription.as_ref(),
)
.await?;
Ok(space)
Expand All @@ -132,6 +139,7 @@ impl Spaces for InMemoryNode {
&space.id,
&space.name,
space.users.iter().map(|u| u.as_ref()).collect(),
space.subscription.as_ref(),
)
.await?;
Ok(space)
Expand Down Expand Up @@ -189,6 +197,7 @@ impl Spaces for InMemoryNode {
&space.id,
&space.name,
space.users.iter().map(|u| u.as_ref()).collect(),
space.subscription.as_ref(),
)
.await?;

Expand Down Expand Up @@ -281,6 +290,7 @@ pub mod tests {
id: String::arbitrary(g),
name: String::arbitrary(g),
users: vec![String::arbitrary(g), String::arbitrary(g)],
subscription: bool::arbitrary(g).then(|| Subscription::arbitrary(g)),
}
}
}
Expand Down
Loading

0 comments on commit 869b9f4

Please sign in to comment.