Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init commit for Prism GET /home #1188

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/alerts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use datafusion::common::tree_node::TreeNode;
use http::StatusCode;
use itertools::Itertools;
use once_cell::sync::Lazy;
use serde::Serialize;
use serde_json::Error as SerdeError;
use std::collections::{HashMap, HashSet};
use std::fmt::{self, Display};
Expand Down Expand Up @@ -873,3 +874,52 @@ impl Alerts {
Ok(())
}
}

#[derive(Debug, Serialize)]
pub struct AlertsInfo {
total: u64,
silenced: u64,
resolved: u64,
triggered: u64,
low: u64,
medium: u64,
high: u64
}

// TODO: add RBAC
pub async fn get_alerts_info() -> Result<AlertsInfo, AlertError> {
let alerts = ALERTS.alerts.read().await;
let mut total = 0;
let mut silenced = 0;
let mut resolved = 0;
let mut triggered = 0;
let mut low = 0;
let mut medium = 0;
let mut high = 0;

for (_, alert) in alerts.iter() {
total += 1;
match alert.state {
AlertState::Silenced => silenced += 1,
AlertState::Resolved => resolved += 1,
AlertState::Triggered => triggered += 1,
}

match alert.severity {
Severity::Low => low += 1,
Severity::Medium => medium += 1,
Severity::High => high += 1,
_ => {}
}
}

Ok(AlertsInfo {
total,
silenced,
resolved,
triggered,
low,
medium,
high
})
}
38 changes: 38 additions & 0 deletions src/handlers/http/home.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Parseable Server (C) 2022 - 2024 Parseable, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

use actix_web::{web, HttpRequest, Responder};

use crate::{home::{generate_home_response, HomeError}, utils::actix::extract_session_key_from_req};


/// Fetches the data to populate Prism's home
///
///
/// # Returns
///
/// A JSONified version of the `HomeResponse` struct.
pub async fn home_api(req: HttpRequest) -> Result<impl Responder, HomeError> {
let key = extract_session_key_from_req(&req)
.map_err(|err| HomeError::Anyhow(anyhow::Error::msg(err.to_string())))?;

let res = generate_home_response(&key)
.await?;

Ok(web::Json(res))
}
1 change: 1 addition & 0 deletions src/handlers/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod audit;
pub mod cluster;
pub mod correlation;
pub mod health_check;
pub mod home;
pub mod ingest;
mod kinesis;
pub mod llm;
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/http/modal/query_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ impl ParseableServer for QueryServer {
.service(Server::get_counts_webscope())
.service(Server::get_metrics_webscope())
.service(Server::get_alerts_webscope())
.service(Self::get_cluster_web_scope()),
.service(Self::get_cluster_web_scope())
.service(Server::get_prism_home()),
)
.service(Server::get_generated());
}
Expand Down
7 changes: 6 additions & 1 deletion src/handlers/http/modal/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ impl ParseableServer for Server {
.service(Self::get_user_role_webscope())
.service(Self::get_counts_webscope())
.service(Self::get_alerts_webscope())
.service(Self::get_metrics_webscope()),
.service(Self::get_metrics_webscope())
.service(Self::get_prism_home()),
)
.service(Self::get_ingest_otel_factory())
.service(Self::get_generated());
Expand Down Expand Up @@ -154,6 +155,10 @@ impl ParseableServer for Server {
}

impl Server {
pub fn get_prism_home() -> Resource {
web::resource("/home").route(web::get().to(http::home::home_api))
}

pub fn get_metrics_webscope() -> Scope {
web::scope("/metrics").service(
web::resource("").route(web::get().to(metrics::get).authorize(Action::Metrics)),
Expand Down
189 changes: 189 additions & 0 deletions src/home/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Parseable Server (C) 2022 - 2024 Parseable, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/


use actix_web::http::header::ContentType;
use chrono::Local;
use http::StatusCode;
use itertools::Itertools;
use serde::Serialize;

use crate::{alerts::{get_alerts_info, AlertsInfo, ALERTS}, correlation::CORRELATIONS, handlers::http::logstream::get_stats_date, parseable::PARSEABLE, rbac::{map::SessionKey, role::Action, Users}, stats::Stats, users::{dashboards::DASHBOARDS, filters::FILTERS}};

#[derive(Debug, Serialize, Default)]
struct StreamInfo {
// stream_count: u32,
// log_source_count: u32,
stats_summary: Stats,
}

#[derive(Debug, Serialize, Default)]
struct DatedStats {
date: String,
events: u64,
ingestion_size: u64,
storage_size: u64,
}

#[derive(Debug, Serialize)]
struct TitleAndId {
title: String,
id: String
}

#[derive(Debug, Serialize)]
pub struct HomeResponse {
alert_titles: Vec<TitleAndId>,
alerts_info: AlertsInfo,
correlation_titles: Vec<TitleAndId>,
stream_info: StreamInfo,
stats_details: Vec<DatedStats>,
stream_titles: Vec<String>,


dashboard_titles: Vec<TitleAndId>,
filter_titles: Vec<TitleAndId>,

}

pub async fn generate_home_response(key: &SessionKey) -> Result<HomeResponse, HomeError> {

let user_id = if let Some(user_id) = Users.get_username_from_session(key) {
user_id
} else {
return Err(HomeError::Anyhow(anyhow::Error::msg("User does not exist")));
};

// get all stream titles
let stream_titles = PARSEABLE.streams
.list()
.iter()
.filter(|logstream| {
Users.authorize(key.clone(), Action::ListStream, Some(&logstream), None) == crate::rbac::Response::Authorized
})
.map(|logstream| logstream.clone())
.collect_vec();

// get all alert titles (TODO: RBAC)
// do we need to move alerts into the PARSEABLE struct?
let alert_titles = ALERTS
.list_alerts_for_user(key.clone())
.await
.map_err(|err| HomeError::Anyhow(anyhow::Error::msg(err.to_string())))?
.iter()
.map(|alert| TitleAndId {
title: alert.title.clone(),
id: alert.id.to_string()
})
.collect_vec();

let correlation_titles = CORRELATIONS
.list_correlations(key)
.await
.map_err(|err| HomeError::Anyhow(anyhow::Error::msg(err.to_string())))?
.iter()
.map(|corr| TitleAndId {
title: corr.title.clone(),
id: corr.id.clone()
})
.collect_vec();

let dashboard_titles = DASHBOARDS
.list_dashboards_by_user(&user_id)
.iter()
.map(|dashboard| TitleAndId {
title: dashboard.name.clone(),
id: dashboard.dashboard_id.as_ref().unwrap().clone()
})
.collect_vec();

let filter_titles = FILTERS
.list_filters_by_user(&user_id)
.iter()
.map(|filter| {
TitleAndId {
title: filter.filter_name.clone(),
id: filter.filter_id.as_ref().unwrap().clone()
}
})
.collect_vec();

let alerts_info = get_alerts_info().await
.map_err(|err| HomeError::Anyhow(anyhow::Error::msg(err.to_string())))?;

let dates = (0..7)
.map(|i| Local::now().checked_sub_signed(chrono::Duration::days(i)).unwrap())
.map(|date| date.format("%Y-%m-%d").to_string())
.collect_vec();

let mut stream_details = Vec::new();

let mut summary = StreamInfo::default();

for date in dates.iter() {
let mut details = DatedStats::default();
details.date = date.clone();

for stream in stream_titles.iter() {
let stats = get_stats_date(stream, &date)
.await
.map_err(|err| HomeError::Anyhow(anyhow::Error::msg(err.to_string())))?;

details.events += stats.events;
details.ingestion_size += stats.ingestion;
details.storage_size += stats.storage;

summary.stats_summary.events += stats.events;
summary.stats_summary.ingestion += stats.ingestion;
summary.stats_summary.storage += stats.storage;
}

stream_details.push(details);
}

Ok(HomeResponse {
stream_info: summary,
stats_details: stream_details,
stream_titles,
alert_titles,
correlation_titles,
dashboard_titles,
filter_titles,
alerts_info
})
}

#[derive(Debug, thiserror::Error)]
pub enum HomeError {
#[error("Error: {0}")]
Anyhow(#[from] anyhow::Error),
}

impl actix_web::ResponseError for HomeError {
fn status_code(&self) -> http::StatusCode {
match self {
HomeError::Anyhow(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}

fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
actix_web::HttpResponse::build(self.status_code())
.insert_header(ContentType::plaintext())
.body(self.to_string())
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod connectors;
pub mod correlation;
mod event;
pub mod handlers;
pub mod home;
pub mod hottier;
mod livetail;
mod metadata;
Expand Down
2 changes: 1 addition & 1 deletion src/users/dashboards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub struct TickConfig {
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct Dashboard {
pub version: Option<String>,
name: String,
pub name: String,
description: String,
pub dashboard_id: Option<String>,
pub user_id: Option<String>,
Expand Down
Loading