new(all): Initial import.

This commit is contained in:
Rodrigo Rodriguez 2024-12-24 10:09:14 -03:00
parent cba43cefde
commit 7f384469a9
4 changed files with 41 additions and 119 deletions

View file

@ -8,11 +8,9 @@ use axum::{
response::IntoResponse,
Json,
};
use gb_core::{Result, Error, models::*};
use gb_messaging::{MessageProcessor, models::MessageEnvelope};
use std::sync::Arc;
use chrono;
use std::{sync::Arc, collections::HashMap};
use tokio::sync::Mutex;
use tracing::{instrument, error};
use uuid::Uuid;
@ -48,7 +46,7 @@ async fn handle_ws_connection(
if let Ok(text) = msg.to_text() {
if let Ok(envelope) = serde_json::from_str::<MessageEnvelope>(text) {
let mut processor = state.message_processor.lock().await;
if let Err(e) = processor.process_message(&envelope).await {
if let Err(e) = processor.process_messages(vec![envelope]).await {
error!("Failed to process message: {}", e);
}
}
@ -57,19 +55,17 @@ async fn handle_ws_connection(
Ok(())
}
#[axum::debug_handler]
#[instrument(skip(state))]
#[instrument(skip_all)]
async fn websocket_handler(
State(state): State<Arc<ApiState>>,
ws: WebSocketUpgrade,
) -> impl IntoResponse {
ws.on_upgrade(|socket| async move {
ws.on_upgrade(move |socket| async move {
let _ = handle_ws_connection(socket, state).await;
})
}
#[axum::debug_handler]
#[instrument(skip(state))]
#[instrument(skip_all)]
async fn send_message(
State(state): State<Arc<ApiState>>,
Json(message): Json<Message>,
@ -77,27 +73,25 @@ async fn send_message(
let envelope = MessageEnvelope {
id: Uuid::new_v4(),
message,
metadata: std::collections::HashMap::new(),
metadata: HashMap::new(),
};
let mut processor = state.message_processor.lock().await;
processor.process_message(&envelope).await
processor.process_messages(vec![envelope.clone()]).await
.map_err(|e| Error::internal(format!("Failed to process message: {}", e)))?;
Ok(Json(MessageId(envelope.id)))
}
#[axum::debug_handler]
#[instrument(skip(state))]
#[instrument(skip_all)]
async fn get_message(
State(_state): State<Arc<ApiState>>,
Path(id): Path<Uuid>,
Path(_id): Path<Uuid>,
) -> Result<Json<Message>> {
todo!()
}
#[axum::debug_handler]
#[instrument(skip(state))]
#[instrument(skip_all)]
async fn create_room(
State(_state): State<Arc<ApiState>>,
Json(_config): Json<RoomConfig>,
@ -105,44 +99,19 @@ async fn create_room(
todo!()
}
#[axum::debug_handler]
#[instrument(skip(state))]
#[instrument(skip_all)]
async fn get_room(
State(_state): State<Arc<ApiState>>,
Path(id): Path<Uuid>,
Path(_id): Path<Uuid>,
) -> Result<Json<Room>> {
todo!()
}
#[axum::debug_handler]
#[instrument(skip(state))]
#[instrument(skip_all)]
async fn join_room(
State(_state): State<Arc<ApiState>>,
Path(id): Path<Uuid>,
Json(user_id): Json<Uuid>,
Path(_id): Path<Uuid>,
Json(_user_id): Json<Uuid>,
) -> Result<Json<Connection>> {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::StatusCode;
use axum::body::Body;
use tower::ServiceExt;
#[tokio::test]
async fn test_health_check() {
let app = create_router(MessageProcessor::new(100));
let response = app
.oneshot(
axum::http::Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
}

View file

@ -1,12 +1,14 @@
use axum::{
http::Request,
response::Response,
http::{Request, Response},
middleware::Next,
body::Body,
};
use axum_extra::TypedHeader;
use axum_extra::headers::{Authorization, authorization::Bearer};
use gb_core::User;
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::{Serialize, Deserialize};
use crate::AuthError;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
@ -14,10 +16,10 @@ struct Claims {
exp: i64,
}
pub async fn auth_middleware<B>(
pub async fn auth_middleware(
TypedHeader(auth): TypedHeader<Authorization<Bearer>>,
request: Request<B>,
next: Next<B>,
request: Request<Body>,
next: Next,
) -> Result<Response, AuthError> {
let token = auth.token();
let key = DecodingKey::from_secret(b"secret");

View file

@ -1,16 +1,10 @@
use std::time::Duration;
use chromiumoxide::browser::{Browser, BrowserConfig};
use chromiumoxide::cdp::browser_protocol;
use chromiumoxide::element::Element;
use chromiumoxide::page::Page;
use futures_util::StreamExt;
use gb_core::{Error, Result};
use tracing::instrument;
#[derive(Debug)]
pub struct Element {
inner: chromiumoxide::element::Element,
}
pub struct WebAutomation {
browser: Browser,
}
@ -18,10 +12,14 @@ pub struct WebAutomation {
impl WebAutomation {
#[instrument]
pub async fn new() -> Result<Self> {
let (browser, mut handler) = Browser::launch(BrowserConfig::default())
let config = BrowserConfig::builder()
.build()
.map_err(|e| Error::internal(e.to_string()))?;
let (browser, mut handler) = Browser::launch(config)
.await
.map_err(|e| Error::internal(e.to_string()))?;
tokio::spawn(async move {
while let Some(h) = handler.next().await {
if let Err(e) = h {
@ -35,36 +33,34 @@ impl WebAutomation {
#[instrument(skip(self))]
pub async fn new_page(&self) -> Result<Page> {
let params = browser_protocol::page::CreateTarget::new();
let page = self.browser.new_page(params)
let params = chromiumoxide::cdp::browser_protocol::target::CreateTarget::new()
.url("about:blank");
self.browser.new_page(params)
.await
.map_err(|e| Error::internal(e.to_string()))?;
Ok(page)
.map_err(|e| Error::internal(e.to_string()))
}
#[instrument(skip(self))]
pub async fn navigate(&self, page: &Page, url: &str) -> Result<()> {
page.goto(url)
.await
.map_err(|e| Error::internal(e.to_string()))?;
Ok(())
.map_err(|e| Error::internal(e.to_string()))
}
#[instrument(skip(self))]
pub async fn get_element(&self, page: &Page, selector: &str) -> Result<Element> {
let element = page.find_element(selector)
page.find_element(selector)
.await
.map_err(|e| Error::internal(e.to_string()))?;
Ok(Element { inner: element })
.map_err(|e| Error::internal(e.to_string()))
}
#[instrument(skip(self))]
pub async fn screenshot(&self, page: &Page, _path: &str) -> Result<Vec<u8>> {
let screenshot_params = browser_protocol::page::CaptureScreenshot::new();
let data = page.screenshot(screenshot_params)
let params = chromiumoxide::cdp::browser_protocol::page::CaptureScreenshot::new();
page.screenshot(params)
.await
.map_err(|e| Error::internal(e.to_string()))?;
Ok(data)
.map_err(|e| Error::internal(e.to_string()))
}
#[instrument(skip(self))]
@ -74,49 +70,4 @@ impl WebAutomation {
.map_err(|e| Error::internal(e.to_string()))?;
Ok(())
}
#[instrument(skip(self))]
pub async fn wait_for_network_idle(&self, page: &Page) -> Result<()> {
page.evaluate("() => new Promise(resolve => setTimeout(resolve, 1000))")
.await
.map_err(|e| Error::internal(e.to_string()))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
#[fixture]
async fn automation() -> WebAutomation {
WebAutomation::new().await.unwrap()
}
#[rstest]
#[tokio::test]
async fn test_navigation(automation: WebAutomation) -> Result<()> {
let page = automation.new_page().await?;
automation.navigate(&page, "https://example.com").await?;
Ok(())
}
#[rstest]
#[tokio::test]
async fn test_element_interaction(automation: WebAutomation) -> Result<()> {
let page = automation.new_page().await?;
automation.navigate(&page, "https://example.com").await?;
let element = automation.get_element(&page, "h1").await?;
Ok(())
}
#[rstest]
#[tokio::test]
async fn test_screenshot(automation: WebAutomation) -> Result<()> {
let page = automation.new_page().await?;
automation.navigate(&page, "https://example.com").await?;
let screenshot = automation.screenshot(&page, "test.png").await?;
Ok(())
}
}

View file

@ -130,7 +130,7 @@ impl ImageProcessor {
let mut api = Tesseract::new(None, Some("eng"))
.map_err(|e| Error::internal(format!("Failed to initialize Tesseract: {}", e)))?;
api.set_image(temp_file.path())
api.set_image(temp_file.path().to_str().unwrap())
.map_err(|e| Error::internal(format!("Failed to set image: {}", e)))?;
api.recognize()