lesspub-query 1.0.2

Query functions for fetching sequences and posts from LessWrong and the EA Forums
Documentation
use anyhow::anyhow;
use cynic::{QueryBuilder, http::ReqwestExt};
use reqwest::Client;
use std::error::Error;

use lesspub_schema::{
    Post, PostArguments, PostQuery, SelectorInput, Sequence, SequenceListInfo, SequencesListQuery,
    SinglePostInput, SingleSequenceArguments, SingleSequenceInput, SingleSequenceQuery,
};

const LW_GRAPHQL_URL: &str = "https://www.lesswrong.com/graphql";
const EA_GRAPHQL_URL: &str = "https://forum.effectivealtruism.org/graphql";

pub async fn get_ea_post(client: &Client, id: &str) -> Result<Post, Box<dyn Error>> {
    let operation = PostQuery::build(PostArguments {
        input: Some(SinglePostInput {
            selector: Some(SelectorInput {
                _id: Some(id.to_string()),
                documentId: None,
            }),
            allowNull: None,
        }),
    });
    let response = client.post(EA_GRAPHQL_URL).run_graphql(operation).await?;
    Ok(response
        .data
        .ok_or(match response.errors {
            Some(errors) => {
                anyhow::Error::msg(errors.first().map(|e| e.message.clone()).unwrap_or(
                    String::from("GraphQL error encountered when fetching ea post"),
                ))
            }
            None => anyhow!("no ea post response found"),
        })?
        .post
        .ok_or(anyhow!("no ea post output found"))?
        .result
        .ok_or(anyhow!("no ea post found"))?)
}

pub async fn get_lw_post(client: &Client, id: &str) -> Result<Post, Box<dyn Error>> {
    let operation = PostQuery::build(PostArguments {
        input: Some(SinglePostInput {
            selector: Some(SelectorInput {
                _id: Some(id.to_string()),
                documentId: None,
            }),
            allowNull: None,
        }),
    });
    let response = client.post(LW_GRAPHQL_URL).run_graphql(operation).await?;
    Ok(response
        .data
        .ok_or(match response.errors {
            Some(errors) => anyhow::Error::msg(
                errors
                    .first()
                    .map(|e| e.message.clone())
                    .unwrap_or(String::from("GraphQL error encountered when fetching post")),
            ),
            None => anyhow!("no post response found"),
        })?
        .post
        .ok_or(anyhow!("no post output found"))?
        .result
        .ok_or(anyhow!("no post found"))?)
}

pub async fn get_lw_sequences(client: &Client) -> Result<Vec<SequenceListInfo>, Box<dyn Error>> {
    let operation = SequencesListQuery::build(());
    let response = client.post(LW_GRAPHQL_URL).run_graphql(operation).await?;
    Ok(response
        .data
        .ok_or(match response.errors {
            Some(errors) => {
                anyhow::Error::msg(errors.first().map(|e| e.message.clone()).unwrap_or(
                    String::from("GraphQL error encountered when fetching sequences list"),
                ))
            }
            None => anyhow!("no sequences list response found"),
        })?
        .sequences
        .ok_or(anyhow!("no sequences list output found"))?
        .results
        .ok_or(anyhow!("no sequences list found"))?
        .into_iter()
        .filter_map(|s| {
            s.map(|sequence| {
                if sequence._id.is_some() {
                    Some(sequence)
                } else {
                    None
                }
            })
            .unwrap()
        })
        .collect())
}

pub async fn get_lw_sequence(client: &Client, id: &str) -> Result<Sequence, Box<dyn Error>> {
    let operation = SingleSequenceQuery::build(SingleSequenceArguments {
        input: Some(SingleSequenceInput {
            selector: Some(SelectorInput {
                _id: Some(id.to_string()),
                documentId: None,
            }),
            allowNull: None,
        }),
    });
    let response = client.post(LW_GRAPHQL_URL).run_graphql(operation).await?;
    if let Some(errors) = response.errors.clone() {
        for error in errors {
            log::error!("{error:?}");
        }
    }
    Ok(response
        .data
        .ok_or(match response.errors {
            Some(errors) => {
                anyhow::Error::msg(errors.first().map(|e| e.message.clone()).unwrap_or(
                    String::from("GraphQL error encountered when fetching lw sequence"),
                ))
            }
            None => anyhow!("no lw sequence response found"),
        })?
        .sequence
        .ok_or(anyhow!("no lw sequence output found"))?
        .result
        .ok_or(anyhow!("no lw sequence found"))?)
}