This commit is contained in:
parent
9aecbfc6fb
commit
90016ea373
4 changed files with 102 additions and 96 deletions
|
@ -17,6 +17,8 @@ mod services;
|
||||||
|
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||||
|
|
||||||
let config = AppConfig::from_env();
|
let config = AppConfig::from_env();
|
||||||
let db_url = config.database_url();
|
let db_url = config.database_url();
|
||||||
let db_custom_url = config.database_custom_url();
|
let db_custom_url = config.database_custom_url();
|
||||||
|
@ -36,7 +38,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
let script_service = ScriptService::new(&app_state.clone());
|
let script_service = ScriptService::new(&app_state.clone());
|
||||||
|
|
||||||
const TEXT : &str = include_str!("prompts/business/data-enrichment.bas");
|
const TEXT: &str = include_str!("prompts/business/data-enrichment.bas");
|
||||||
|
|
||||||
match script_service.compile(TEXT) {
|
match script_service.compile(TEXT) {
|
||||||
Ok(ast) => match script_service.run(&ast) {
|
Ok(ast) => match script_service.run(&ast) {
|
||||||
|
@ -48,10 +50,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
|
|
||||||
let cors = Cors::default()
|
let cors = Cors::default()
|
||||||
.send_wildcard()
|
.send_wildcard()
|
||||||
.allowed_origin("*")
|
|
||||||
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
|
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
|
||||||
.allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
|
.allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
|
||||||
.allowed_header(header::CONTENT_TYPE)
|
.allowed_header(header::CONTENT_TYPE)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
let items = FIND "gb.rob", "ACTION=EMUL1"
|
let items = FIND "gb.rob", "ACTION=EMUL1"
|
||||||
FOR EACH item IN items
|
FOR EACH item IN items
|
||||||
let text = GET "example.com"
|
let text = GET "https://pragmatismo.com.br"
|
||||||
PRINT item.name
|
PRINT item.company
|
||||||
NEXT item
|
NEXT item
|
|
@ -1,20 +1,40 @@
|
||||||
use rhai::Dynamic;
|
use rhai::{Dynamic, Engine};
|
||||||
use rhai::Engine;
|
use reqwest;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::services::state::AppState;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
pub fn get_keyword(_state: &AppState, engine: &mut Engine) {
|
pub fn get_keyword(_state: &AppState, engine: &mut Engine) {
|
||||||
engine
|
engine.register_custom_syntax(
|
||||||
.register_custom_syntax(
|
&["GET", "$expr$"],
|
||||||
&["GET", "$expr$"],
|
false, // Expression, not statement
|
||||||
false, // Expression, not statement
|
move |context, inputs| {
|
||||||
|context, inputs| {
|
let url = context.eval_expression_tree(&inputs[0])?;
|
||||||
let url = context.eval_expression_tree(&inputs[0])?;
|
let url_str = url.to_string();
|
||||||
let url_str = url.to_string();
|
|
||||||
|
|
||||||
println!("GET executed: {}", url_str.to_string());
|
if url_str.starts_with("https") {
|
||||||
Ok(format!("Content from {}", url_str).into())
|
println!("HTTPS GET request: {}", url_str);
|
||||||
},
|
|
||||||
)
|
// Use the same pattern as find_keyword
|
||||||
.unwrap();
|
let fut = execute_get(&url_str);
|
||||||
|
let result = tokio::task::block_in_place(|| {
|
||||||
|
tokio::runtime::Handle::current().block_on(fut)
|
||||||
|
}).map_err(|e| format!("HTTP request failed: {}", e))?;
|
||||||
|
|
||||||
|
Ok(Dynamic::from(result))
|
||||||
|
} else {
|
||||||
|
println!("GET executed: {}", url_str);
|
||||||
|
Ok(Dynamic::from(format!("Content from {}", url_str)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn execute_get(url: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
|
||||||
|
println!("Starting execute_get with URL: {}", url);
|
||||||
|
|
||||||
|
let response = reqwest::get(url).await?;
|
||||||
|
let content = response.text().await?;
|
||||||
|
|
||||||
|
println!("GET request successful, got {} bytes", content.len());
|
||||||
|
Ok(format!("Secure content fetched: {}", content))
|
||||||
|
}
|
|
@ -1,104 +1,90 @@
|
||||||
use smartstring::SmartString;
|
use log::{debug, warn};
|
||||||
use rhai::{Array, Dynamic};
|
use rhai::{Array, Dynamic};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
use smartstring::SmartString;
|
||||||
use sqlx::Column; // Required for .name() method
|
use sqlx::Column; // Required for .name() method
|
||||||
use sqlx::TypeInfo; // Required for .type_info() method
|
use sqlx::TypeInfo; // Required for .type_info() method
|
||||||
use sqlx::{postgres::PgRow, Row};
|
use sqlx::{postgres::PgRow, Row};
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use sqlx::{Decode, Type};
|
||||||
|
|
||||||
pub fn row_to_json(row: PgRow) -> Result<Value, Box<dyn Error>> {
|
pub fn row_to_json(row: PgRow) -> Result<Value, Box<dyn Error>> {
|
||||||
let mut result = serde_json::Map::new();
|
let mut result = serde_json::Map::new();
|
||||||
let columns = row.columns();
|
let columns = row.columns();
|
||||||
println!("Processing {} columns", columns.len());
|
debug!("Converting row with {} columns", columns.len());
|
||||||
|
|
||||||
for (i, column) in columns.iter().enumerate() {
|
for (i, column) in columns.iter().enumerate() {
|
||||||
let column_name = column.name();
|
let column_name = column.name();
|
||||||
let type_name = column.type_info().name();
|
let type_name = column.type_info().name();
|
||||||
println!(
|
|
||||||
"Processing column {}: {} (type: {})",
|
|
||||||
i, column_name, type_name
|
|
||||||
);
|
|
||||||
|
|
||||||
let value: Value = match type_name {
|
let value = match type_name {
|
||||||
"INT4" | "INT8" | "int4" | "int8" => match row.try_get::<i64, _>(i) {
|
"INT4" | "int4" => handle_nullable_type::<i32>(&row, i, column_name),
|
||||||
Ok(v) => {
|
"INT8" | "int8" => handle_nullable_type::<i64>(&row, i, column_name),
|
||||||
println!("Got int64 value: {}", v);
|
"FLOAT4" | "float4" => handle_nullable_type::<f32>(&row, i, column_name),
|
||||||
json!(v)
|
"FLOAT8" | "float8" => handle_nullable_type::<f64>(&row, i, column_name),
|
||||||
}
|
"TEXT" | "VARCHAR" | "text" | "varchar" => handle_nullable_type::<String>(&row, i, column_name),
|
||||||
Err(e) => {
|
"BOOL" | "bool" => handle_nullable_type::<bool>(&row, i, column_name),
|
||||||
println!("Failed to get int64, trying i32: {}", e);
|
"JSON" | "JSONB" | "json" | "jsonb" => handle_json(&row, i, column_name),
|
||||||
match row.try_get::<i32, _>(i) {
|
|
||||||
Ok(v) => json!(v as i64),
|
|
||||||
Err(_) => Value::Null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FLOAT4" | "FLOAT8" | "float4" | "float8" => match row.try_get::<f64, _>(i) {
|
|
||||||
Ok(v) => {
|
|
||||||
println!("Got float64 value: {}", v);
|
|
||||||
json!(v)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to get float64, trying f32: {}", e);
|
|
||||||
match row.try_get::<f32, _>(i) {
|
|
||||||
Ok(v) => json!(v as f64),
|
|
||||||
Err(_) => Value::Null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TEXT" | "VARCHAR" | "text" | "varchar" => match row.try_get::<String, _>(i) {
|
|
||||||
Ok(v) => {
|
|
||||||
println!("Got string value: {}", v);
|
|
||||||
json!(v)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to get string: {}", e);
|
|
||||||
Value::Null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"BOOL" | "bool" => match row.try_get::<bool, _>(i) {
|
|
||||||
Ok(v) => {
|
|
||||||
println!("Got bool value: {}", v);
|
|
||||||
json!(v)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to get bool: {}", e);
|
|
||||||
Value::Null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"JSON" | "JSONB" | "json" | "jsonb" => match row.try_get::<Value, _>(i) {
|
|
||||||
Ok(v) => {
|
|
||||||
println!("Got JSON value: {:?}", v);
|
|
||||||
v
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to get JSON, trying as string: {}", e);
|
|
||||||
match row.try_get::<String, _>(i) {
|
|
||||||
Ok(s) => match serde_json::from_str(&s) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(_) => json!(s),
|
|
||||||
},
|
|
||||||
Err(_) => Value::Null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unknown type {}, trying as string", type_name);
|
warn!("Unknown type {} for column {}", type_name, column_name);
|
||||||
match row.try_get::<String, _>(i) {
|
handle_nullable_type::<String>(&row, i, column_name)
|
||||||
Ok(v) => json!(v),
|
|
||||||
Err(_) => Value::Null,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
result.insert(column_name.to_string(), value);
|
result.insert(column_name.to_string(), value);
|
||||||
}
|
}
|
||||||
println!("Finished processing row, got {} fields", result.len());
|
|
||||||
Ok(Value::Object(result))
|
Ok(Value::Object(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_nullable_type<'r, T>(row: &'r PgRow, idx: usize, col_name: &str) -> Value
|
||||||
|
where
|
||||||
|
T: Type<sqlx::Postgres> + Decode<'r, sqlx::Postgres> + serde::Serialize + std::fmt::Debug,
|
||||||
|
{
|
||||||
|
match row.try_get::<Option<T>, _>(idx) {
|
||||||
|
Ok(Some(val)) => {
|
||||||
|
debug!("Successfully read column {} as {:?}", col_name, val);
|
||||||
|
json!(val)
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
debug!("Column {} is NULL", col_name);
|
||||||
|
Value::Null
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to read column {}: {}", col_name, e);
|
||||||
|
Value::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn handle_json(row: &PgRow, idx: usize, col_name: &str) -> Value {
|
||||||
|
// First try to get as Option<Value>
|
||||||
|
match row.try_get::<Option<Value>, _>(idx) {
|
||||||
|
Ok(Some(val)) => {
|
||||||
|
debug!("Successfully read JSON column {} as Value", col_name);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
Ok(None) => return Value::Null,
|
||||||
|
Err(_) => (), // Fall through to other attempts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try as Option<String> that might contain JSON
|
||||||
|
match row.try_get::<Option<String>, _>(idx) {
|
||||||
|
Ok(Some(s)) => match serde_json::from_str(&s) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(_) => {
|
||||||
|
debug!("Column {} contains string that's not JSON", col_name);
|
||||||
|
json!(s)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(None) => Value::Null,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to read JSON column {}: {}", col_name, e);
|
||||||
|
Value::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn json_value_to_dynamic(value: &Value) -> Dynamic {
|
pub fn json_value_to_dynamic(value: &Value) -> Dynamic {
|
||||||
match value {
|
match value {
|
||||||
Value::Null => Dynamic::UNIT,
|
Value::Null => Dynamic::UNIT,
|
||||||
|
|
Loading…
Add table
Reference in a new issue