Commit 470cfa30 authored by Wohlgemuth, Jason's avatar Wohlgemuth, Jason
Browse files

feat: Remove more TODO comments (only 3 remain); Update bag capability and add...

feat: Remove more TODO comments (only 3 remain); Update bag capability and add body data to invoke requests
parent 7dd00f76
Loading
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
---
name: coding-guidelines
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria. These guidelines should be applied to all coding tasks.
license: MIT
---

@@ -65,3 +65,19 @@ For multi-step tasks, state a brief plan:
```

Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.

## 5. Always prefer functional programming style when possible

**Minimize moving parts. Avoid mutable state and side effects.**

- Prioritize the 3 T's of functional programming:
  1. Immutability
  2. Composability
  3. Purity
- Avoid mutable state and side effects.
- Minimize use of `return` statements and early exits. Instead, structure code to flow naturally from inputs to outputs.
- Favor pure functions that take inputs and return outputs without modifying external state.
- Use immutable data structures and avoid shared mutable state.
- This leads to clearer, more predictable code that is easier to test and debug.

OOP makes code understandable by encapsulating moving parts. Functional Programming makes code understandable by minimizing moving parts.
 No newline at end of file
+0 −2
Original line number Diff line number Diff line
@@ -1164,7 +1164,6 @@
                            },
                            {
                                "name": "Vulnerability Science",
                                "alternativeName": "VS",
                                "additionalType": "group",
                                "member": []
                            }
@@ -1804,7 +1803,6 @@
                            },
                            {
                                "name": "Macromolecular Nanomaterials",
                                "alternativeName": "Macro",
                                "additionalType": "group",
                                "member": []
                            },
+9 −4
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ use crate::io::{get, InputOutput};
use crate::prelude;
use crate::prelude::{create_dir_all, remove_file, Error, File, HashMap, PathBuf, Write};
use crate::schema::research_activity::ResearchActivity;
use crate::schema::{ImageObject, MediaObject, ProgrammingLanguage, VideoObject, Website};
use crate::schema::{ImageObject, MediaObject, Organization, ProgrammingLanguage, VideoObject, Website};
use crate::skip;
use crate::util::constants::{
    APPLICATION, CUSTOM_VALE_PACKAGE_NAME, DEFAULT_VALE_PACKAGE_URL, DEFAULT_VALE_ROOT, DISABLED_VALE_RULES, ENABLED_VALE_PACKAGES, ORGANIZATION,
@@ -165,7 +165,6 @@ pub struct Check {
}
#[async_trait]
impl Analysis for ResearchActivity {
    // TODO: Refactor to use "gather" capability on RAD as text (although might be less efficient?)
    /// Checks links associated with research activity (DOIs, websites, contact URL) for availability and readability
    #[cfg(feature = "std")]
    async fn check_websites(paths: Vec<PathBuf>, is_offline: bool) -> Vec<Check> {
@@ -720,12 +719,18 @@ impl StaticAnalyzer<ValeConfig> for Vale {
                }
                match File::create(format!("{parent}/accept.txt")) {
                    | Ok(mut file) => {
                        // TODO: Concatenate organization alternative names to accept file
                        let acronyms = Constant::last_values("acronyms");
                        let partners = Constant::last_values("partners");
                        let sponsors = Constant::last_values("sponsors");
                        let abbreviations = Organization::alternative_names();
                        let words = Constant::read_lines("accept.txt");
                        let content = acronyms.chain(partners).chain(sponsors).chain(words).collect::<Vec<String>>().join("\n");
                        let content = acronyms
                            .chain(partners)
                            .chain(sponsors)
                            .chain(abbreviations)
                            .chain(words)
                            .collect::<Vec<String>>()
                            .join("\n");
                        file.write_all(content.as_bytes()).expect("Unable to write to accept.txt");
                    }
                    | Err(why) => panic!("=> {} Create accept.txt - {why}", Label::fail()),
+31 −4
Original line number Diff line number Diff line
@@ -40,6 +40,11 @@ pub trait EndpointSearch {
    /// Filter list of endpoints by name and return the first match
    fn find_by_name(&self, value: impl Into<String>) -> Option<Endpoint>;
}
/// Helper trait for converting parameter collections into HTTP request body
pub trait IntoBody {
    /// Convert this value into a `serde_json::Value` for request body, using only body-style parameters.
    fn into_body(self) -> serde_json::Value;
}
/// Helper trait for converting parameter collections into HTTP headers
pub trait IntoHeaders {
    /// Convert this value into a `reqwest::HeaderMap`, using only header-style parameters.
@@ -428,6 +433,27 @@ impl Param {
        rendered.unwrap_or_default()
    }
}
impl IntoBody for Vec<Param> {
    fn into_body(self) -> serde_json::Value {
        let body = self
            .into_iter()
            .filter(|param| param.is_body())
            .map(|param| {
                let Param { name, values, .. } = param;
                let flattened: Vec<String> = values.into_iter().flat_map(|vec| vec.into_iter().flatten()).collect();
                let value = if flattened.len() == 1 {
                    serde_json::Value::String(flattened.into_iter().next().unwrap())
                } else if flattened.is_empty() {
                    serde_json::Value::Null
                } else {
                    serde_json::Value::Array(flattened.into_iter().map(serde_json::Value::String).collect())
                };
                (name, value)
            })
            .collect();
        serde_json::Value::Object(body)
    }
}
impl IntoHeaders for Vec<Param> {
    fn into_headers(self) -> HeaderMap {
        let mut headers = HeaderMap::new();
@@ -553,14 +579,15 @@ impl RemoteResource for Endpoint {
        match resource {
            | Some(Resource { method, template, .. }) => {
                let path = tera.render_str(template, &context).unwrap_or_default();
                let params = data.unwrap_or_default();
                let headers = params.clone().into_headers();
                let body = params.into_body();
                let request = match method {
                    | HttpMethod::Get => get(path),
                    // TODO: Pass Body params for POST and PUT requests
                    | HttpMethod::Post => post(path),
                    | HttpMethod::Put => put(path),
                    | HttpMethod::Post => post(path).json(&body),
                    | HttpMethod::Put => put(path).json(&body),
                    | _ => unimplemented!("Only GET, POST, and PUT methods are currently implemented"),
                };
                let headers = data.unwrap_or_default().into_headers();
                match request.headers(headers).send().await {
                    | Ok(response) => match response.text().await {
                        | Ok(text) => {
+36 −0
Original line number Diff line number Diff line
@@ -467,3 +467,39 @@ fn test_ror_search_response() {
    let response: ror::SearchResponse = serde_json::from_str(&json).expect("Failed to deserialize JSON");
    insta::assert_snapshot!(format!("{:#?}", response));
}
#[test]
fn test_params_into_body() {
    use crate::io::api::IntoBody;
    // Test single value body param
    let params = vec![Param::of_type(ParamStyle::Body)
        .values(vec![vec![Some("test value")]])
        .with_key("message")];
    let body = params.into_body();
    assert_eq!(body["message"], serde_json::Value::String("test value".to_string()));
    // Test multiple values body param (should be an array)
    let params = vec![Param::of_type(ParamStyle::Body)
        .values(vec![vec![Some("value1")], vec![Some("value2")]])
        .with_key("items")];
    let body = params.into_body();
    assert_eq!(body["items"], serde_json::json!(["value1", "value2"]));
    // Test empty body param (should be null)
    let params = vec![Param::of_type(ParamStyle::Body).values(vec![]).with_key("empty")];
    let body = params.into_body();
    assert_eq!(body["empty"], serde_json::Value::Null);
    // Test mixed params (only body params should be included)
    let params = vec![
        Param::of_type(ParamStyle::Body)
            .values(vec![vec![Some("body value")]])
            .with_key("body_field"),
        Param::of_type(ParamStyle::QueryPair)
            .values(vec![vec![Some("query"), Some("value")]])
            .with_key("q"),
        Param::of_type(ParamStyle::Header)
            .values(vec![vec![Some("Bearer token")]])
            .with_key("Authorization"),
    ];
    let body = params.into_body();
    assert_eq!(body["body_field"], serde_json::Value::String("body value".to_string()));
    assert!(body.get("q").is_none());
    assert!(body.get("Authorization").is_none());
}
Loading