Skip to content

Commit

Permalink
feat: find (soon to be) invalid project names
Browse files Browse the repository at this point in the history
  • Loading branch information
chesedo committed Nov 16, 2022
1 parent e10f096 commit bb62bf9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
3 changes: 3 additions & 0 deletions admin/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ pub struct Args {
pub enum Command {
/// Try to revive projects in the crashed state
Revive,

/// Manage project names
ProjectNames,
}
16 changes: 16 additions & 0 deletions admin/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ impl Client {
self.post("/admin/revive").await
}

pub async fn list_invalid_project_names(&self) -> Result<String> {
self.get("/admin/invalid-names").await
}

async fn post(&self, path: &str) -> Result<String> {
reqwest::Client::new()
.post(format!("{}{}", self.api_url, path))
Expand All @@ -25,4 +29,16 @@ impl Client {
.await
.context("failed to post text body from response")
}

async fn get(&self, path: &str) -> Result<String> {
reqwest::Client::new()
.get(format!("{}{}", self.api_url, path))
.bearer_auth(&self.api_key)
.send()
.await
.context("failed to make post request")?
.text()
.await
.context("failed to post text body from response")
}
}
4 changes: 4 additions & 0 deletions admin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ async fn main() {

let res = match args.command {
Command::Revive => client.revive().await.expect("revive to succeed"),
Command::ProjectNames => client
.list_invalid_project_names()
.await
.expect("get invalid project names"),
};

println!("{res}");
Expand Down
68 changes: 68 additions & 0 deletions gateway/src/api/latest.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;

Expand Down Expand Up @@ -186,6 +187,72 @@ async fn revive_projects(
.map_err(|_| Error::from_kind(ErrorKind::Internal))
}

async fn invalid_project_names(
_: Admin,
Extension(service): Extension<Arc<GatewayService>>,
) -> Result<AxumJson<Vec<(String, Vec<String>)>>, Error> {
let projects: HashMap<String, String, std::collections::hash_map::RandomState> =
HashMap::from_iter(
service
.iter_projects_with_user()
.await?
.map(|(project_name, account_name)| (project_name.0, account_name.0)),
);
let mut result = Vec::with_capacity(projects.len());

for (project_name, account_name) in &projects {
let mut output = Vec::new();
let cleaned_name = project_name.to_lowercase();

// Were there any uppercase characters
if &cleaned_name != project_name {
// Since there were uppercase characters, will the new name clash with any existing projects
if let Some(other_account) = projects.get(&cleaned_name) {
if other_account == account_name {
output.push("changing to lower case will clash with same owner".to_string());
} else {
output.push(format!(
"changing to lower case will clash with another owner: {other_account}"
));
}
}
}

let cleaned_separator_name = cleaned_name.trim_matches('-');
// Were there any dash cleanups
if cleaned_separator_name != cleaned_name {
// Since there were dash cleanups, will the new name clash with any existing projects
if let Some(other_account) = projects.get(cleaned_separator_name) {
if other_account == account_name {
output.push("cleaning dashes will clash with same owner".to_string());
} else {
output.push(format!(
"cleaning dashes will clash with another owner: {other_account}"
));
}
}
}

// Are reserved words used
match cleaned_separator_name {
"shuttleapp" | "shuttle" => output.push("is a reserved name".to_string()),
_ => {}
}

// Is it longer than 63 chars
if cleaned_separator_name.len() > 63 {
output.push("final name is too long".to_string());
}

// Only report of problem projects
if !output.is_empty() {
result.push((project_name.to_string(), output));
}
}

Ok(AxumJson(result))
}

pub fn make_api(service: Arc<GatewayService>, sender: Sender<BoxedTask>) -> Router<Body> {
debug!("making api route");

Expand All @@ -201,6 +268,7 @@ pub fn make_api(service: Arc<GatewayService>, sender: Sender<BoxedTask>) -> Rout
.route("/users/:account_name", get(get_user).post(post_user))
.route("/projects/:project/*any", any(route_project))
.route("/admin/revive", post(revive_projects))
.route("/admin/invalid-names", get(invalid_project_names))
.layer(Extension(service))
.layer(Extension(sender))
.layer(
Expand Down
24 changes: 24 additions & 0 deletions gateway/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,22 @@ impl GatewayService {
Ok(project_name)
}

pub async fn iter_projects_with_user(
&self,
) -> Result<impl Iterator<Item = (ProjectName, AccountName)>, Error> {
let iter = query("SELECT project_name, account_name FROM projects")
.fetch_all(&self.db)
.await?
.into_iter()
.map(|row| {
(
row.try_get("project_name").unwrap(),
row.try_get("account_name").unwrap(),
)
});
Ok(iter)
}

pub fn context(&self) -> GatewayContext {
self.provider.context()
}
Expand Down Expand Up @@ -610,6 +626,14 @@ pub mod tests {
assert!(creating_same_project_name(&project, &matrix));

assert_eq!(svc.find_project(&matrix).await.unwrap(), project);
assert_eq!(
svc.iter_projects_with_user()
.await
.unwrap()
.next()
.expect("to get one project with its user"),
(matrix.clone(), neo.clone())
);

let mut work = svc
.new_task()
Expand Down

0 comments on commit bb62bf9

Please sign in to comment.