Skip to content

Commit 6397b71

Browse files
committed
updates to prism endpoints
/logstream/{logstream}/info - added stats and retention to the response body GET /users GET /users/{username}
1 parent 3e5077a commit 6397b71

File tree

5 files changed

+232
-7
lines changed

5 files changed

+232
-7
lines changed

src/handlers/http/modal/query_server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ impl ParseableServer for QueryServer {
6161
.service(Server::get_about_factory())
6262
.service(Self::get_logstream_webscope())
6363
.service(Self::get_user_webscope())
64+
.service(Server::get_users_webscope())
6465
.service(Server::get_dashboards_webscope())
6566
.service(Server::get_filters_webscope())
6667
.service(Server::get_llm_webscope())

src/handlers/http/modal/server.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ impl ParseableServer for Server {
8181
.service(Self::get_about_factory())
8282
.service(Self::get_logstream_webscope())
8383
.service(Self::get_user_webscope())
84+
.service(Self::get_users_webscope())
8485
.service(Self::get_dashboards_webscope())
8586
.service(Self::get_filters_webscope())
8687
.service(Self::get_llm_webscope())
@@ -170,7 +171,9 @@ impl Server {
170171
web::resource("/info").route(
171172
web::get()
172173
.to(http::prism_logstream::get_info)
173-
.authorize_for_stream(Action::GetStreamInfo),
174+
.authorize_for_stream(Action::GetStreamInfo)
175+
.authorize_for_stream(Action::GetStats)
176+
.authorize_for_stream(Action::GetRetention),
174177
),
175178
),
176179
)
@@ -497,6 +500,27 @@ impl Server {
497500
)
498501
}
499502

503+
// get the users webscope (for Prism only)
504+
pub fn get_users_webscope() -> Scope {
505+
web::scope("/users")
506+
.service(
507+
web::resource("")
508+
// GET /users => List all users
509+
.route(
510+
web::get()
511+
.to(http::rbac::list_users_prism)
512+
.authorize(Action::ListUser),
513+
),
514+
)
515+
.service(
516+
web::resource("/{username}").route(
517+
web::get()
518+
.to(http::rbac::get_prism_user)
519+
.authorize_for_user(Action::GetUserRoles),
520+
),
521+
)
522+
}
523+
500524
// get the user webscope
501525
pub fn get_user_webscope() -> Scope {
502526
web::scope("/user")

src/handlers/http/rbac.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@
1919
use std::collections::{HashMap, HashSet};
2020

2121
use crate::{
22-
rbac::{map::roles, role::model::DefaultPrivilege, user, Users},
22+
rbac::{self, map::roles, role::model::DefaultPrivilege, user, Users, UsersPrism},
2323
storage::ObjectStorageError,
2424
validator::{self, error::UsernameValidationError},
2525
};
26-
use actix_web::{http::header::ContentType, web, Responder};
26+
use actix_web::{
27+
http::header::ContentType,
28+
web::{self, Path},
29+
Responder,
30+
};
2731
use http::StatusCode;
32+
use itertools::Itertools;
2833
use tokio::sync::Mutex;
2934

3035
use super::modal::utils::rbac_utils::{get_metadata, put_metadata};
@@ -58,6 +63,88 @@ pub async fn list_users() -> impl Responder {
5863
web::Json(Users.collect_user::<User>())
5964
}
6065

66+
/// Handler for GET /api/v1/users
67+
/// returns list of all registerd users along with their roles and other info
68+
pub async fn list_users_prism() -> impl Responder {
69+
// get all users
70+
let prism_users = rbac::map::users()
71+
.values()
72+
.map(|u| {
73+
let (id, method, email, picture) = match &u.ty {
74+
user::UserType::Native(_) => (u.username(), "native", None, None),
75+
user::UserType::OAuth(oauth) => (
76+
u.username(),
77+
"oauth",
78+
oauth.user_info.email.clone(),
79+
oauth.user_info.picture.clone(),
80+
),
81+
};
82+
let roles: HashMap<String, Vec<DefaultPrivilege>> = Users
83+
.get_role(id)
84+
.iter()
85+
.filter_map(|role_name| {
86+
roles()
87+
.get(role_name)
88+
.map(|role| (role_name.to_owned(), role.clone()))
89+
})
90+
.collect();
91+
92+
UsersPrism {
93+
id: id.into(),
94+
method: method.into(),
95+
email,
96+
picture,
97+
roles,
98+
}
99+
})
100+
.collect_vec();
101+
102+
web::Json(prism_users)
103+
}
104+
105+
/// Function for GET /users/{username}
106+
pub async fn get_prism_user(username: Path<String>) -> Result<impl Responder, RBACError> {
107+
let username = username.into_inner();
108+
let prism_user = rbac::map::users()
109+
.values()
110+
.map(|u| {
111+
let (id, method, email, picture) = match &u.ty {
112+
user::UserType::Native(_) => (u.username(), "native", None, None),
113+
user::UserType::OAuth(oauth) => (
114+
u.username(),
115+
"oauth",
116+
oauth.user_info.email.clone(),
117+
oauth.user_info.picture.clone(),
118+
),
119+
};
120+
let roles: HashMap<String, Vec<DefaultPrivilege>> = Users
121+
.get_role(id)
122+
.iter()
123+
.filter_map(|role_name| {
124+
roles()
125+
.get(role_name)
126+
.map(|role| (role_name.to_owned(), role.clone()))
127+
})
128+
.collect();
129+
130+
UsersPrism {
131+
id: id.into(),
132+
method: method.into(),
133+
email,
134+
picture,
135+
roles,
136+
}
137+
})
138+
.filter(|u| u.id.eq(&username))
139+
.collect_vec();
140+
141+
if prism_user.is_empty() {
142+
Err(RBACError::UserDoesNotExist)
143+
} else {
144+
Ok(web::Json(prism_user[0].clone()))
145+
}
146+
}
147+
61148
// Handler for POST /api/v1/user/{username}
62149
// Creates a new user by username if it does not exists
63150
pub async fn post_user(

src/prism/logstream/mod.rs

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,54 @@ use std::sync::Arc;
2020

2121
use actix_web::http::header::ContentType;
2222
use arrow_schema::Schema;
23+
use chrono::Utc;
2324
use http::StatusCode;
2425
use serde::Serialize;
2526

2627
use crate::{
27-
handlers::http::{logstream::error::StreamError, query::update_schema_when_distributed},
28+
handlers::http::{
29+
cluster::utils::{merge_quried_stats, IngestionStats, QueriedStats, StorageStats},
30+
logstream::error::StreamError,
31+
query::update_schema_when_distributed,
32+
},
2833
parseable::{StreamNotFound, PARSEABLE},
29-
storage::StreamInfo,
34+
stats,
35+
storage::{retention::Retention, StreamInfo},
3036
LOCK_EXPECT,
3137
};
3238

3339
#[derive(Serialize)]
3440
pub struct PrismLogstreamInfo {
3541
info: StreamInfo,
3642
schema: Arc<Schema>,
43+
stats: QueriedStats,
44+
retention: Retention,
3745
}
3846

3947
pub async fn get_prism_logstream_info(
4048
stream_name: &str,
4149
) -> Result<PrismLogstreamInfo, PrismLogstreamError> {
4250
// get StreamInfo
4351
let info = get_stream_info_helper(stream_name).await?;
52+
4453
// get stream schema
4554
let schema = get_stream_schema_helper(stream_name).await?;
4655

47-
Ok(PrismLogstreamInfo { info, schema })
56+
// get stream stats
57+
let stats = get_stats(stream_name).await?;
58+
59+
// get retention
60+
let retention = PARSEABLE
61+
.get_stream(stream_name)?
62+
.get_retention()
63+
.unwrap_or_default();
64+
65+
Ok(PrismLogstreamInfo {
66+
info,
67+
schema,
68+
stats,
69+
retention,
70+
})
4871
}
4972

5073
async fn get_stream_schema_helper(stream_name: &str) -> Result<Arc<Schema>, StreamError> {
@@ -66,6 +89,73 @@ async fn get_stream_schema_helper(stream_name: &str) -> Result<Arc<Schema>, Stre
6689
}
6790
}
6891

92+
async fn get_stats(stream_name: &str) -> Result<QueriedStats, PrismLogstreamError> {
93+
let stats = stats::get_current_stats(stream_name, "json")
94+
.ok_or_else(|| StreamNotFound(stream_name.to_owned()))?;
95+
96+
let ingestor_stats: Option<Vec<QueriedStats>> = None;
97+
98+
let hash_map = PARSEABLE.streams.read().expect("Readable");
99+
let stream_meta = &hash_map
100+
.get(stream_name)
101+
.ok_or_else(|| StreamNotFound(stream_name.to_owned()))?
102+
.metadata
103+
.read()
104+
.expect(LOCK_EXPECT);
105+
106+
let time = Utc::now();
107+
108+
let stats = match &stream_meta.first_event_at {
109+
Some(_) => {
110+
let ingestion_stats = IngestionStats::new(
111+
stats.current_stats.events,
112+
format!("{} {}", stats.current_stats.ingestion, "Bytes"),
113+
stats.lifetime_stats.events,
114+
format!("{} {}", stats.lifetime_stats.ingestion, "Bytes"),
115+
stats.deleted_stats.events,
116+
format!("{} {}", stats.deleted_stats.ingestion, "Bytes"),
117+
"json",
118+
);
119+
let storage_stats = StorageStats::new(
120+
format!("{} {}", stats.current_stats.storage, "Bytes"),
121+
format!("{} {}", stats.lifetime_stats.storage, "Bytes"),
122+
format!("{} {}", stats.deleted_stats.storage, "Bytes"),
123+
"parquet",
124+
);
125+
126+
QueriedStats::new(stream_name, time, ingestion_stats, storage_stats)
127+
}
128+
129+
None => {
130+
let ingestion_stats = IngestionStats::new(
131+
stats.current_stats.events,
132+
format!("{} {}", stats.current_stats.ingestion, "Bytes"),
133+
stats.lifetime_stats.events,
134+
format!("{} {}", stats.lifetime_stats.ingestion, "Bytes"),
135+
stats.deleted_stats.events,
136+
format!("{} {}", stats.deleted_stats.ingestion, "Bytes"),
137+
"json",
138+
);
139+
let storage_stats = StorageStats::new(
140+
format!("{} {}", stats.current_stats.storage, "Bytes"),
141+
format!("{} {}", stats.lifetime_stats.storage, "Bytes"),
142+
format!("{} {}", stats.deleted_stats.storage, "Bytes"),
143+
"parquet",
144+
);
145+
146+
QueriedStats::new(stream_name, time, ingestion_stats, storage_stats)
147+
}
148+
};
149+
let stats = if let Some(mut ingestor_stats) = ingestor_stats {
150+
ingestor_stats.push(stats);
151+
merge_quried_stats(ingestor_stats)
152+
} else {
153+
stats
154+
};
155+
156+
Ok(stats)
157+
}
158+
69159
async fn get_stream_info_helper(stream_name: &str) -> Result<StreamInfo, StreamError> {
70160
// For query mode, if the stream not found in memory map,
71161
//check if it exists in the storage
@@ -120,13 +210,16 @@ pub enum PrismLogstreamError {
120210
Anyhow(#[from] anyhow::Error),
121211
#[error("StreamError: {0}")]
122212
StreamError(#[from] StreamError),
213+
#[error("StreamNotFound: {0}")]
214+
StreamNotFound(#[from] StreamNotFound),
123215
}
124216

125217
impl actix_web::ResponseError for PrismLogstreamError {
126218
fn status_code(&self) -> http::StatusCode {
127219
match self {
128220
PrismLogstreamError::Anyhow(_) => StatusCode::INTERNAL_SERVER_ERROR,
129221
PrismLogstreamError::StreamError(e) => e.status_code(),
222+
PrismLogstreamError::StreamNotFound(_) => StatusCode::INTERNAL_SERVER_ERROR,
130223
}
131224
}
132225

src/rbac/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ pub mod map;
2020
pub mod role;
2121
pub mod user;
2222

23-
use std::collections::HashSet;
23+
use std::collections::{HashMap, HashSet};
2424

2525
use chrono::{DateTime, Days, Utc};
2626
use itertools::Itertools;
27+
use role::model::DefaultPrivilege;
28+
use serde::Serialize;
29+
use url::Url;
2730

2831
use crate::rbac::map::{mut_sessions, mut_users, sessions, users};
2932
use crate::rbac::role::Action;
@@ -166,6 +169,23 @@ impl Users {
166169
}
167170
}
168171

172+
/// This struct represents a user along with their roles, email, etc
173+
///
174+
/// TODO: rename this after deprecating the older struct
175+
#[derive(Debug, Serialize, Clone)]
176+
pub struct UsersPrism {
177+
// username
178+
pub id: String,
179+
// oaith or native
180+
pub method: String,
181+
// email only if method is oauth
182+
pub email: Option<String>,
183+
// picture only if oauth
184+
pub picture: Option<Url>,
185+
// roles for the user
186+
pub roles: HashMap<String, Vec<DefaultPrivilege>>,
187+
}
188+
169189
fn roles_to_permission(roles: Vec<String>) -> Vec<Permission> {
170190
let mut perms = HashSet::new();
171191
for role in &roles {

0 commit comments

Comments
 (0)