mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-28 19:59:46 -05:00
305 lines
7.0 KiB
Rust
305 lines
7.0 KiB
Rust
use std::collections::{HashMap, HashSet};
|
|
|
|
use anyhow::Context;
|
|
use async_timing_util::unix_timestamp_ms;
|
|
use database::mungos::{
|
|
find::find_collect,
|
|
mongodb::{bson::doc, options::FindOptions},
|
|
};
|
|
use futures_util::TryStreamExt;
|
|
use komodo_client::{
|
|
api::read::*,
|
|
entities::{
|
|
Operation,
|
|
build::{Build, BuildActionState, BuildListItem, BuildState},
|
|
permission::PermissionLevel,
|
|
update::UpdateStatus,
|
|
},
|
|
};
|
|
use mogh_resolver::Resolve;
|
|
|
|
use crate::{
|
|
helpers::query::get_all_tags,
|
|
permission::get_check_permissions,
|
|
resource,
|
|
state::{action_states, build_state_cache, db_client},
|
|
};
|
|
|
|
use super::ReadArgs;
|
|
|
|
impl Resolve<ReadArgs> for GetBuild {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<Build> {
|
|
Ok(
|
|
get_check_permissions::<Build>(
|
|
&self.build,
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
)
|
|
.await?,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Resolve<ReadArgs> for ListBuilds {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<Vec<BuildListItem>> {
|
|
let all_tags = if self.query.tags.is_empty() {
|
|
vec![]
|
|
} else {
|
|
get_all_tags(None).await?
|
|
};
|
|
Ok(
|
|
resource::list_for_user::<Build>(
|
|
self.query,
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
&all_tags,
|
|
)
|
|
.await?,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Resolve<ReadArgs> for ListFullBuilds {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<ListFullBuildsResponse> {
|
|
let all_tags = if self.query.tags.is_empty() {
|
|
vec![]
|
|
} else {
|
|
get_all_tags(None).await?
|
|
};
|
|
Ok(
|
|
resource::list_full_for_user::<Build>(
|
|
self.query,
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
&all_tags,
|
|
)
|
|
.await?,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Resolve<ReadArgs> for GetBuildActionState {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<BuildActionState> {
|
|
let build = get_check_permissions::<Build>(
|
|
&self.build,
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
)
|
|
.await?;
|
|
let action_state = action_states()
|
|
.build
|
|
.get(&build.id)
|
|
.await
|
|
.unwrap_or_default()
|
|
.get()?;
|
|
Ok(action_state)
|
|
}
|
|
}
|
|
|
|
impl Resolve<ReadArgs> for GetBuildsSummary {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<GetBuildsSummaryResponse> {
|
|
let builds = resource::list_full_for_user::<Build>(
|
|
Default::default(),
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
&[],
|
|
)
|
|
.await
|
|
.context("failed to get all builds")?;
|
|
|
|
let mut res = GetBuildsSummaryResponse::default();
|
|
|
|
let cache = build_state_cache();
|
|
let action_states = action_states();
|
|
|
|
for build in builds {
|
|
res.total += 1;
|
|
|
|
match (
|
|
cache.get(&build.id).await.unwrap_or_default(),
|
|
action_states
|
|
.build
|
|
.get(&build.id)
|
|
.await
|
|
.unwrap_or_default()
|
|
.get()?,
|
|
) {
|
|
(_, action_states) if action_states.building => {
|
|
res.building += 1;
|
|
}
|
|
(BuildState::Ok, _) => res.ok += 1,
|
|
(BuildState::Failed, _) => res.failed += 1,
|
|
(BuildState::Unknown, _) => res.unknown += 1,
|
|
// will never come off the cache in the building state, since that comes from action states
|
|
(BuildState::Building, _) => unreachable!(),
|
|
}
|
|
}
|
|
|
|
Ok(res)
|
|
}
|
|
}
|
|
|
|
const ONE_DAY_MS: i64 = 86400000;
|
|
|
|
impl Resolve<ReadArgs> for GetBuildMonthlyStats {
|
|
async fn resolve(
|
|
self,
|
|
_: &ReadArgs,
|
|
) -> mogh_error::Result<GetBuildMonthlyStatsResponse> {
|
|
let curr_ts = unix_timestamp_ms() as i64;
|
|
let next_day = curr_ts - curr_ts % ONE_DAY_MS + ONE_DAY_MS;
|
|
|
|
let close_ts = next_day - self.page as i64 * 30 * ONE_DAY_MS;
|
|
let open_ts = close_ts - 30 * ONE_DAY_MS;
|
|
|
|
let mut build_updates = db_client()
|
|
.updates
|
|
.find(doc! {
|
|
"start_ts": {
|
|
"$gte": open_ts,
|
|
"$lt": close_ts
|
|
},
|
|
"operation": Operation::RunBuild.to_string(),
|
|
})
|
|
.await
|
|
.context("failed to get updates cursor")?;
|
|
|
|
let mut days = HashMap::<i64, BuildStatsDay>::with_capacity(32);
|
|
|
|
let mut curr = open_ts;
|
|
|
|
while curr < close_ts {
|
|
let stats = BuildStatsDay {
|
|
ts: curr as f64,
|
|
..Default::default()
|
|
};
|
|
days.insert(curr, stats);
|
|
curr += ONE_DAY_MS;
|
|
}
|
|
|
|
while let Some(update) = build_updates.try_next().await? {
|
|
if let Some(end_ts) = update.end_ts {
|
|
let day = update.start_ts - update.start_ts % ONE_DAY_MS;
|
|
let entry = days.entry(day).or_default();
|
|
entry.count += 1.0;
|
|
entry.time += ms_to_hour(end_ts - update.start_ts);
|
|
}
|
|
}
|
|
|
|
Ok(GetBuildMonthlyStatsResponse::new(
|
|
days.into_values().collect(),
|
|
))
|
|
}
|
|
}
|
|
|
|
const MS_TO_HOUR_DIVISOR: f64 = 1000.0 * 60.0 * 60.0;
|
|
fn ms_to_hour(duration: i64) -> f64 {
|
|
duration as f64 / MS_TO_HOUR_DIVISOR
|
|
}
|
|
|
|
impl Resolve<ReadArgs> for ListBuildVersions {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<Vec<BuildVersionResponseItem>> {
|
|
let ListBuildVersions {
|
|
build,
|
|
major,
|
|
minor,
|
|
patch,
|
|
limit,
|
|
} = self;
|
|
let build = get_check_permissions::<Build>(
|
|
&build,
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
)
|
|
.await?;
|
|
|
|
let mut filter = doc! {
|
|
"target": {
|
|
"type": "Build",
|
|
"id": build.id
|
|
},
|
|
"operation": Operation::RunBuild.to_string(),
|
|
"status": UpdateStatus::Complete.to_string(),
|
|
"success": true
|
|
};
|
|
if let Some(major) = major {
|
|
filter.insert("version.major", major);
|
|
}
|
|
if let Some(minor) = minor {
|
|
filter.insert("version.minor", minor);
|
|
}
|
|
if let Some(patch) = patch {
|
|
filter.insert("version.patch", patch);
|
|
}
|
|
|
|
let versions = find_collect(
|
|
&db_client().updates,
|
|
filter,
|
|
FindOptions::builder()
|
|
.sort(doc! { "_id": -1 })
|
|
.limit(limit)
|
|
.build(),
|
|
)
|
|
.await
|
|
.context("failed to pull versions from mongo")?
|
|
.into_iter()
|
|
.map(|u| (u.version, u.start_ts))
|
|
.filter(|(v, _)| !v.is_none())
|
|
.map(|(version, ts)| BuildVersionResponseItem { version, ts })
|
|
.collect();
|
|
Ok(versions)
|
|
}
|
|
}
|
|
|
|
impl Resolve<ReadArgs> for ListCommonBuildExtraArgs {
|
|
async fn resolve(
|
|
self,
|
|
ReadArgs { user }: &ReadArgs,
|
|
) -> mogh_error::Result<ListCommonBuildExtraArgsResponse> {
|
|
let all_tags = if self.query.tags.is_empty() {
|
|
vec![]
|
|
} else {
|
|
get_all_tags(None).await?
|
|
};
|
|
let builds = resource::list_full_for_user::<Build>(
|
|
self.query,
|
|
user,
|
|
PermissionLevel::Read.into(),
|
|
&all_tags,
|
|
)
|
|
.await
|
|
.context("failed to get resources matching query")?;
|
|
|
|
// first collect with guaranteed uniqueness
|
|
let mut res = HashSet::<String>::new();
|
|
|
|
for build in builds {
|
|
for extra_arg in build.config.extra_args {
|
|
res.insert(extra_arg);
|
|
}
|
|
}
|
|
|
|
let mut res = res.into_iter().collect::<Vec<_>>();
|
|
res.sort();
|
|
Ok(res)
|
|
}
|
|
}
|