use std::collections::HashMap;
use anyhow::anyhow;
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
use crate::constants;
use crate::expression::Expression;
use crate::split::CommitConfig;
use crate::split::SplitMap;
use crate::types::InitQuery;
use crate::types::Message;
#[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
#[serde(rename_all = "lowercase")]
pub enum Language {
Python,
Rust,
Go,
}
#[derive(Deserialize)]
pub struct CodegenFile {
pub name: String,
pub content: String,
}
#[derive(Deserialize)]
pub struct CodegenResponse {
pub files: Vec<CodegenFile>,
pub messages: Vec<Message>,
}
#[derive(Deserialize, Debug, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InitializationData {
pub reduced_expression: Expression,
pub hash: String,
pub commit_config: CommitConfig,
pub splits: SplitMap,
pub commit_id: u64,
}
pub struct HashResponse {
pub commit_id: u64,
pub hash: String,
}
pub async fn codegen_request(
token: &str,
branch_name: Option<String>,
language: Language,
sdk_version: String,
query: Option<&str>,
include_token: bool,
include_fallback: bool,
base_url: &str,
) -> Result<CodegenResponse, reqwest::Error> {
let body = json!({
"query": query,
"includeToken": include_token,
"includeFallback": include_fallback,
"sdkType": language,
"sdkVersion": sdk_version,
"language": language,
})
.to_string();
let mut params = HashMap::new();
params.insert("token", token);
params.insert("body", &body);
if let Some(ref branch_name) = branch_name {
params.insert("branch", branch_name.as_str());
}
let client = reqwest::Client::new();
client
.get(format!("{}/codegen", base_url))
.query(¶ms)
.send()
.await?
.error_for_status()?
.json()
.await
}
pub async fn init_request(
token: &str,
branch_name: Option<String>,
query: &InitQuery,
variables: &Value,
language: Language,
base_url: &str,
) -> Result<InitializationData, reqwest::Error> {
let body = &json!({
"query": query,
"variables": variables,
"sdkType": language,
"sdkVersion": constants::VERSION,
})
.to_string();
let mut params = HashMap::new();
params.insert("token", token);
params.insert("body", body);
if let Some(ref branch_name) = branch_name {
params.insert("branch", branch_name.as_str());
}
let client = reqwest::Client::new();
client
.get(format!("{}/init", base_url))
.query(¶ms)
.send()
.await?
.error_for_status()?
.json()
.await
}
pub async fn hash_request(
token: &str,
branch_name: Option<String>,
query: &InitQuery,
variables: &Value,
language: Language,
base_url: &str,
) -> Result<HashResponse, anyhow::Error> {
let body = &json!({
"query": query,
"variables": variables,
"sdkType": language,
"sdkVersion": constants::VERSION,
})
.to_string();
let mut params = HashMap::new();
params.insert("token", token);
params.insert("body", body);
if let Some(ref branch_name) = branch_name {
params.insert("branch", branch_name.as_str());
}
let client = reqwest::Client::new();
let text = client
.get(format!("{}/hash", base_url))
.query(¶ms)
.send()
.await?
.error_for_status()?
.text()
.await?;
if let Some((raw_commit_id, hash)) = text.split_once('_') {
let commit_id = raw_commit_id.parse::<u64>()?;
Ok(HashResponse {
commit_id,
hash: hash.to_string(),
})
} else {
Err(anyhow!("Invalid hash response: {}", text))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::expression::BooleanExpression;
use crate::expression::Logs;
use crate::expression::ObjectExpression;
use std::collections::HashMap;
#[test]
fn test_deserialization() {
let json = r#"
{
"commitId": 3535,
"hash": "7719744257597328",
"reducedExpression": {
"id": "ih422aM1N7w5u1pZHjiHj",
"type": "ObjectExpression",
"fields": {
"root": {
"id": "gZDqtndJoiI8RQ7q4CuCo",
"logs": {
"evaluations": {
"gy-OTD4_b0zH2XOmaXctc": 1
}
},
"type": "ObjectExpression",
"fields": {
"simpleFlag": {
"id": "3Py1edtYsW_dEqdwEewak",
"logs": {
"evaluations": {
"WO2MDkDuq0TMU5_MvKSe6": 1
}
},
"type": "BooleanExpression",
"value": false,
"valueType": {
"type": "BooleanValueType"
}
}
},
"valueType": {
"type": "ObjectValueType",
"objectTypeName": "Root"
},
"objectTypeName": "Root"
}
},
"metadata": {
"permissions": {
"user": {},
"group": {
"team": {
"write": "allow"
}
}
}
},
"valueType": {
"type": "ObjectValueType",
"objectTypeName": "Query"
},
"objectTypeName": "Query"
},
"splits": {},
"commitConfig": {
"splitConfig": {}
}
}
"#;
let deserialized: InitializationData = serde_json::from_str(json).unwrap();
let expected = InitializationData {
commit_id: 3535,
hash: "7719744257597328".to_string(),
commit_config: CommitConfig::new(),
splits: SplitMap::new(),
reduced_expression: Expression::Object(ObjectExpression {
id: "ih422aM1N7w5u1pZHjiHj".to_string(),
object_type_name: "Query".to_string(),
logs: None,
is_transient: false,
fields: HashMap::from([(
"root".to_string(),
Expression::Object(ObjectExpression {
id: "gZDqtndJoiI8RQ7q4CuCo".to_string(),
object_type_name: "Root".to_string(),
is_transient: false,
logs: Some(Logs {
evaluations: Some(HashMap::from([(
"gy-OTD4_b0zH2XOmaXctc".to_string(),
1,
)])),
event_list: None,
exposure_list: None,
}),
fields: HashMap::from([(
"simpleFlag".to_string(),
Expression::Boolean(BooleanExpression {
id: "3Py1edtYsW_dEqdwEewak".to_string(),
value: false,
is_transient: false,
logs: Some(Logs {
evaluations: Some(HashMap::from([(
"WO2MDkDuq0TMU5_MvKSe6".to_string(),
1,
)])),
event_list: None,
exposure_list: None,
}),
}),
)]),
}),
)]),
}),
};
assert_eq!(deserialized, expected)
}
}