Skip to content

Commit

Permalink
Merge pull request #130 from itowlson/traitify-cloud-helpers
Browse files Browse the repository at this point in the history
Move some cloud functions to an extension
  • Loading branch information
itowlson authored Oct 15, 2023
2 parents 0cfb477 + 75cbad7 commit a33d7d3
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 109 deletions.
2 changes: 1 addition & 1 deletion crates/cloud/src/client_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use uuid::Uuid;

#[cfg_attr(feature = "mocks", mockall::automock)]
#[async_trait]
pub trait CloudClientInterface {
pub trait CloudClientInterface: Send + Sync {
async fn create_device_code(&self, client_id: Uuid) -> Result<DeviceCodeItem>;

async fn login(&self, token: String) -> Result<TokenInfo>;
Expand Down
76 changes: 76 additions & 0 deletions crates/cloud/src/cloud_client_extensions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use uuid::Uuid;

use crate::CloudClientInterface;

#[async_trait]
pub trait CloudClientExt {
async fn get_app_id(&self, app_name: &str) -> Result<Option<Uuid>>;
async fn get_revision_id(&self, app_id: Uuid, version: &str) -> Result<Uuid>;
async fn get_channel_id(&self, app_id: Uuid, channel_name: &str) -> Result<Uuid>;
}

#[async_trait]
impl<T: CloudClientInterface> CloudClientExt for T {
async fn get_app_id(&self, app_name: &str) -> Result<Option<Uuid>> {
let apps_vm = self
.list_apps(crate::DEFAULT_APPLIST_PAGE_SIZE, None)
.await
.context("Could not fetch apps")?;
let app = apps_vm.items.iter().find(|&x| x.name == app_name);
Ok(app.map(|a| a.id))
}

async fn get_revision_id(&self, app_id: Uuid, version: &str) -> Result<Uuid> {
let mut revisions = self.list_revisions().await?;

loop {
if let Some(revision) = revisions
.items
.iter()
.find(|&x| x.revision_number == version && x.app_id == app_id)
{
return Ok(revision.id);
}

if revisions.is_last_page {
break;
}

revisions = self.list_revisions_next(&revisions).await?;
}

Err(anyhow!(
"No revision with version {} and app id {}",
version,
app_id
))
}

async fn get_channel_id(&self, app_id: Uuid, channel_name: &str) -> Result<Uuid> {
let mut channels_vm = self.list_channels().await?;

loop {
if let Some(channel) = channels_vm
.items
.iter()
.find(|&x| x.app_id == app_id && x.name == channel_name)
{
return Ok(channel.id);
}

if channels_vm.is_last_page {
break;
}

channels_vm = self.list_channels_next(&channels_vm).await?;
}

Err(anyhow!(
"No channel with app_id {} and name {}",
app_id,
channel_name,
))
}
}
5 changes: 5 additions & 0 deletions crates/cloud/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
pub mod client;
mod client_interface;
mod cloud_client_extensions;

pub use client_interface::CloudClientInterface;
#[cfg(feature = "mocks")]
pub use client_interface::MockCloudClientInterface;
pub use cloud_client_extensions::CloudClientExt;

pub const DEFAULT_APPLIST_PAGE_SIZE: i32 = 50;
2 changes: 1 addition & 1 deletion src/commands/apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::commands::{client_and_app_id, create_cloud_client};
use crate::opts::*;
use anyhow::{Context, Result};
use clap::{Args, Parser};
use cloud::CloudClientInterface;
use cloud::{CloudClientInterface, DEFAULT_APPLIST_PAGE_SIZE};
use cloud_openapi::models::{AppItem, AppItemPage, ValidationStatus};

#[derive(Parser, Debug)]
Expand Down
100 changes: 8 additions & 92 deletions src/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
use clap::Parser;
use cloud::{
client::{Client as CloudClient, ConnectionConfig},
CloudClientInterface,
CloudClientExt, CloudClientInterface,
};
use cloud_openapi::models::{
ChannelRevisionSelectionStrategy as CloudChannelRevisionSelectionStrategy, Database,
Expand All @@ -25,10 +25,7 @@ use url::Url;
use uuid::Uuid;

use crate::{
commands::{
get_app_id_cloud,
variables::{get_variables, set_variables},
},
commands::variables::{get_variables, set_variables},
random_name::RandomNameGenerator,
spin,
};
Expand Down Expand Up @@ -196,7 +193,7 @@ impl DeployCommand {
println!("Deploying...");

// Create or update app
let channel_id = match get_app_id_cloud(&client, &name).await? {
let channel_id = match client.get_app_id(&name).await? {
Some(app_id) => {
let labels = application.sqlite_databases();
if !labels.is_empty()
Expand All @@ -210,12 +207,10 @@ impl DeployCommand {
client
.add_revision(storage_id.clone(), version.clone())
.await?;
let existing_channel_id = self
.get_channel_id_cloud(&client, SPIN_DEPLOY_CHANNEL_NAME.to_string(), app_id)
.await?;
let active_revision_id = self
.get_revision_id_cloud(&client, version.clone(), app_id)
let existing_channel_id = client
.get_channel_id(app_id, SPIN_DEPLOY_CHANNEL_NAME)
.await?;
let active_revision_id = client.get_revision_id(app_id, &version).await?;
client
.patch_channel(
existing_channel_id,
Expand Down Expand Up @@ -259,9 +254,7 @@ impl DeployCommand {
.add_revision(storage_id.clone(), version.clone())
.await?;

let active_revision_id = self
.get_revision_id_cloud(&client, version.clone(), app_id)
.await?;
let active_revision_id = client.get_revision_id(app_id, &version).await?;

let channel_id = client
.add_channel(
Expand Down Expand Up @@ -398,7 +391,7 @@ impl DeployCommand {
}

// Are all remaining required variables satisfied by variables already in the cloud?
let extant_variables = match self.try_get_app_id_cloud(client, name.to_string()).await {
let extant_variables = match client.get_app_id(name).await {
Ok(Some(app_id)) => match get_variables(client, app_id).await {
Ok(variables) => variables,
Err(_) => {
Expand Down Expand Up @@ -427,83 +420,6 @@ impl DeployCommand {
Err(anyhow!("The application requires values for the following variable(s) which have not been set: {list_text}. Use the --variable flag to provide values."))
}

async fn try_get_app_id_cloud(
&self,
cloud_client: &CloudClient,
name: String,
) -> Result<Option<Uuid>> {
let apps_vm = cloud_client
.list_apps(DEFAULT_APPLIST_PAGE_SIZE, None)
.await?;
let app = apps_vm.items.iter().find(|&x| x.name == name.clone());
match app {
Some(a) => Ok(Some(a.id)),
None => Ok(None),
}
}

async fn get_revision_id_cloud(
&self,
cloud_client: &CloudClient,
version: String,
app_id: Uuid,
) -> Result<Uuid> {
let mut revisions = cloud_client.list_revisions().await?;

loop {
if let Some(revision) = revisions
.items
.iter()
.find(|&x| x.revision_number == version && x.app_id == app_id)
{
return Ok(revision.id);
}

if revisions.is_last_page {
break;
}

revisions = cloud_client.list_revisions_next(&revisions).await?;
}

Err(anyhow!(
"No revision with version {} and app id {}",
version,
app_id
))
}

async fn get_channel_id_cloud(
&self,
cloud_client: &CloudClient,
name: String,
app_id: Uuid,
) -> Result<Uuid> {
let mut channels_vm = cloud_client.list_channels().await?;

loop {
if let Some(channel) = channels_vm
.items
.iter()
.find(|&x| x.app_id == app_id && x.name == name.clone())
{
return Ok(channel.id);
}

if channels_vm.is_last_page {
break;
}

channels_vm = cloud_client.list_channels_next(&channels_vm).await?;
}

Err(anyhow!(
"No channel with app_id {} and name {}",
app_id,
name
))
}

async fn push_oci(
&self,
application: DeployableApp,
Expand Down
18 changes: 4 additions & 14 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ pub mod login;
pub mod sqlite;
pub mod variables;

use crate::{commands::deploy::login_connection, opts::DEFAULT_APPLIST_PAGE_SIZE};
use crate::commands::deploy::login_connection;
use anyhow::{Context, Result};
use cloud::{
client::{Client as CloudClient, ConnectionConfig},
CloudClientInterface,
CloudClientExt,
};
use uuid::Uuid;

Expand All @@ -23,23 +23,13 @@ pub(crate) async fn create_cloud_client(deployment_env_id: Option<&str>) -> Resu
Ok(CloudClient::new(connection_config))
}

pub(crate) async fn get_app_id_cloud(
cloud_client: &CloudClient,
name: &str,
) -> Result<Option<Uuid>> {
let apps_vm = CloudClient::list_apps(cloud_client, DEFAULT_APPLIST_PAGE_SIZE, None)
.await
.context("Could not fetch apps")?;
let app = apps_vm.items.iter().find(|&x| x.name == name);
Ok(app.map(|a| a.id))
}

async fn client_and_app_id(
deployment_env_id: Option<&str>,
app: &str,
) -> Result<(CloudClient, Uuid)> {
let client = create_cloud_client(deployment_env_id).await?;
let app_id = get_app_id_cloud(&client, app)
let app_id = client
.get_app_id(app)
.await
.with_context(|| format!("Error finding app_id for app '{}'", app))?
.with_context(|| format!("Could not find app '{}'", app))?;
Expand Down
1 change: 0 additions & 1 deletion src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ pub const CLOUD_URL_ENV: &str = "CLOUD_URL";
pub const DEPLOYMENT_ENV_NAME_ENV: &str = "FERMYON_DEPLOYMENT_ENVIRONMENT";
pub const TOKEN: &str = "TOKEN";
pub const SPIN_AUTH_TOKEN: &str = "SPIN_AUTH_TOKEN";
pub const DEFAULT_APPLIST_PAGE_SIZE: i32 = 50;

0 comments on commit a33d7d3

Please sign in to comment.