Skip to content

Commit

Permalink
Test corrections and slack openid adaptations
Browse files Browse the repository at this point in the history
  • Loading branch information
mettke committed Nov 10, 2020
1 parent 118a40e commit 0cae326
Show file tree
Hide file tree
Showing 183 changed files with 2,073 additions and 956 deletions.
5 changes: 0 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@ features = [
"reqwest_blocking",
]

[[example]]
name = "start_rtm"
path = "examples/start_rtm.rs"
required-features = ["reqwest"]

[[example]]
name = "channel_history"
path = "examples/channel_history.rs"
Expand Down
130 changes: 130 additions & 0 deletions codegen/src/adapt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#![allow(clippy::single_match)]

use crate::rust::{Method, Module, ResponseType};

pub fn correct(modules: &mut [Module]) {
for module in modules {
match module.name.as_str() {
"conversations" => correct_conversations(module),
_ => {}
}
}
}

fn correct_conversations(module: &mut Module) {
for mut method in &mut module.methods {
match method.name.as_str() {
"history" => correct_conversations_history(&mut method),
"info" => correct_conversations_info(&mut method),
"list" => correct_conversations_list(&mut method),
_ => {}
}
}
}

fn correct_conversations_info(method: &mut Method) {
for mut param in &mut method.parameters {
match param.name.as_str() {
// The channel parameter is required
"channel" => param.required = true,
// The Token parameter is required
"token" => param.required = true,
_ => {}
}
}
if let ResponseType::Object(o) = &mut method.response.r#type {
for t in o {
match t.name.as_str() {
// channel is defined as Vec<_> but returns a single _
"channel" => {
if let ResponseType::Vec(v) = &mut t.r#type.r#type {
t.r#type = (**v).clone();
}
}
_ => {}
}
}
}
}

fn correct_conversations_list(method: &mut Method) {
for mut param in &mut method.parameters {
match param.name.as_str() {
// The Token parameter is required
"token" => param.required = true,
_ => {}
}
}
if let ResponseType::Object(o) = &mut method.response.r#type {
for t in o {
match t.name.as_str() {
// channels is defined as Vec<Vec<_>> but the endpoint returns Vec<_>
"channels" => dedup_vec(&mut t.r#type.r#type),
_ => {}
}
}
}
}

fn correct_conversations_history(method: &mut Method) {
for mut param in &mut method.parameters {
match param.name.as_str() {
// The channel parameter is required
"channel" => param.required = true,
// The Token parameter is required
"token" => param.required = true,
_ => {}
}
}
if let ResponseType::Object(o) = &mut method.response.r#type {
for t in o {
match t.name.as_str() {
// messages can be null
"messages" => {
t.r#type.required = false;
if let ResponseType::Vec(v) = &mut t.r#type.r#type {
if let ResponseType::Object(o) = &mut v.r#type {
for t in o {
match t.name.as_str() {
// attachments is not a vec
"attachments" => {
if let ResponseType::Vec(v) = &mut t.r#type.r#type {
if let ResponseType::Object(o) = &mut v.r#type {
for t in o {
match t.name.as_str() {
// id is not required
"id" => t.r#type.required = false,
_ => {}
}
}
}
}
}
// bot_id is not a vec
"bot_id" => {
if let ResponseType::Vec(v) = &t.r#type.r#type {
t.r#type = (**v).clone();
t.r#type.required = false;
}
}
_ => {}
}
}
}
}
}
// channel_actions_ts can be null
"channel_actions_ts" => t.r#type.required = false,
_ => {}
}
}
}
}

fn dedup_vec(r#type: &mut ResponseType) {
if let ResponseType::Vec(v) = r#type {
if let ResponseType::Vec(vi) = &v.r#type {
*v = vi.clone();
}
}
}
29 changes: 6 additions & 23 deletions codegen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@

use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::fs;
use std::io::Write;
use std::iter::Peekable;
use std::path::{Path, PathBuf};

use anyhow::{bail, Context, Result};
use clap::{App, Arg};
use reqwest::blocking::Client;
use serde::Serialize;

mod rust;
use rust::{GenMode, HttpMethod, Method, Module, ModuleBuilder, Parameter, Response};

mod schema;
use schema::{EnumValues, Operation, PathItem, Spec};

mod adapt;
use adapt::correct;

mod vec_or_single;

const DEFAULT_OUT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../src");
Expand All @@ -33,9 +33,8 @@ fn main() -> Result<()> {
let arguments = handle_arguments()?;
let mut spec = fetch_slack_api_spec()?;
spec.replace_refs()?;
let modules = transform_to_modules(&spec)?;
debug(&modules);
debug_json(&spec.paths);
let mut modules = transform_to_modules(&spec)?;
correct(&mut modules);
generate(&arguments.outdir, &modules)?;
Ok(())
}
Expand Down Expand Up @@ -78,21 +77,6 @@ fn fetch_slack_api_spec() -> Result<Spec> {
.context("Unable to deserialize slack server response")
}

fn debug<D: fmt::Debug>(data: D) {
std::fs::File::create("/tmp_crypt/slack.debug")
.unwrap()
.write_all(format!("{:#?}", data).as_bytes())
.unwrap();
}

fn debug_json<D: Serialize>(data: D) {
let data = serde_json::to_string_pretty(&data).expect("Unable to serialize debug data");
std::fs::File::create("/tmp_crypt/slack.json")
.unwrap()
.write_all(data.as_bytes())
.unwrap();
}

fn transform_to_modules(spec: &Spec) -> Result<Vec<Module>> {
let mut modules: HashMap<&str, ModuleBuilder> = HashMap::new();
for (full_name, path) in &spec.paths {
Expand Down Expand Up @@ -219,8 +203,7 @@ fn create_method(
};
for parameter in &op.parameters {
let parameter = match parameter.location.as_ref() {
"header" if parameter.name == "token" => continue,
"query" if parameter.name == "token" => continue,
"header" if parameter.name == "token" => Parameter::try_from(parameter)?,
"formData" | "query" => Parameter::try_from(parameter)?,
loc => bail!(format!(
"Unsupported paramter location {} for {}",
Expand Down
55 changes: 45 additions & 10 deletions codegen/src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,9 @@ impl Module {
};
let data = format!(
"{header}
#![allow(unused_variables)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(clippy::match_single_binding)]
#![allow(clippy::blacklisted_name)]
{modules}{imports}
Expand Down Expand Up @@ -290,6 +290,7 @@ impl Method {
let parameters = self
.parameters
.iter()
.filter(|p| p.name != "token")
.map(Parameter::to_rust)
.collect::<Vec<_>>()
.join("\n");
Expand Down Expand Up @@ -415,6 +416,7 @@ impl Method {
HttpMethod::Get => self
.parameters
.iter()
.filter(|p| p.name != "token")
.map(Parameter::to_rust_fn)
.collect::<Vec<_>>()
.join("\n"),
Expand All @@ -426,34 +428,59 @@ impl Method {
.collect::<Vec<_>>()
.join("\n"),
};
let token = self.parameters.iter().find(|p| p.name == "token");
let headers = match self.http_method {
HttpMethod::Get => "",
HttpMethod::Post => {
if let Some(token) = self.parameters.iter().find(|p| p.name == "token") {
if let Some(token) = token {
if token.required {
", &[(\"token\", request.token.clone())]"
", &[(\"token\", token.to_string())]"
} else {
", &request.token.as_ref().map_or(vec![], |t| vec![(\"token\", t.into())])"
", &token.map_or(vec![], |t| vec![(\"token\", t.to_string())])"
}
} else {
", &[]"
}
}
};
let params = match self.http_method {
HttpMethod::Post => "",
HttpMethod::Get => {
if let Some(token) = token {
if token.required {
"Some((\"token\", token.to_string())),"
} else {
"token.map(|token| (\"token\", token.to_string())),"
}
} else {
""
}
}
};
let token_param = if let Some(token) = token {
if token.required {
"token: &str,"
} else {
"token: Option<&str>,"
}
} else {
""
};
let empty_param = if parameters.is_empty() { "_" } else { "" };
let out = format!(
"/// {description}
///
/// Wraps {doc_url}
pub {fn_type} {fn_name}<R>(
client: &R,
request: &{request_type},
client: &R,{token_param}
{empty_param}request: &{request_type},
) -> Result<{response_type}, {error_type}<R::Error>>
where
R: SlackWebRequestSender,
{{
let params = vec![
{parameters}
{params}{parameters}
];
let params: Vec<(&str, String)> = params.into_iter().filter_map(|x| x).collect::<Vec<_>>();
let url = crate::get_slack_url_for_method(\"{full_name}\");
Expand All @@ -477,6 +504,9 @@ impl Method {
parameters = parameters,
method=self.http_method.method(),
headers=headers,
token_param=token_param,
params = params,
empty_param = empty_param
);
Ok(out)
}
Expand Down Expand Up @@ -538,6 +568,7 @@ impl TryFrom<&schema::Parameter> for Parameter {
#[derive(Clone, Debug)]
pub enum ParameterDataType {
Bool,
Decimal,
Int,
String,
}
Expand All @@ -546,6 +577,7 @@ impl ParameterDataType {
pub fn to_rust(&self, required: bool) -> String {
let r#type = match self {
Self::Bool => "bool",
Self::Decimal => "f64",
Self::Int => "u64",
Self::String => "String",
};
Expand All @@ -563,7 +595,8 @@ impl FromStr for ParameterDataType {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let r#type = match s {
"boolean" => Self::Bool,
"integer" | "number" => Self::Int,
"integer" => Self::Int,
"number" => Self::Decimal,
"string" => Self::String,
t => bail!(format!("Type {} currently not supported", t)),
};
Expand All @@ -590,6 +623,7 @@ impl Response {
let res = match &self.r#type {
// Inner
ResponseType::Bool if !top => ("bool".into(), Vec::new()),
ResponseType::Decimal if !top => ("f64".into(), Vec::new()),
ResponseType::Int if !top => ("u64".into(), Vec::new()),
ResponseType::String if !top => ("String".into(), Vec::new()),
ResponseType::RawJson if !top => ("serde_json::Value".into(), Vec::new()),
Expand Down Expand Up @@ -742,6 +776,7 @@ impl TryFrom<(&str, &schema::Schema)> for Member {
#[derive(Clone, Debug)]
pub enum ResponseType {
Bool,
Decimal,
Int,
String,
Object(Vec<Member>),
Expand Down Expand Up @@ -770,7 +805,7 @@ impl TryFrom<&schema::Schema> for ResponseType {
// Primitives
(Some(schema), _, _) if schema.contains(&"boolean") => Self::Bool,
(Some(schema), _, _) if schema.contains(&"integer") => Self::Int,
(Some(schema), _, _) if schema.contains(&"number") => Self::Int,
(Some(schema), _, _) if schema.contains(&"number") => Self::Decimal,
(Some(schema), _, _) if schema.contains(&"string") => Self::String,

// Object
Expand Down
8 changes: 4 additions & 4 deletions examples/channel_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
let client = slack::default_client().map_err(|_| "Could not get default_client")?;

let response = slack::channels::history(
let response = slack::conversations::history(
&client,
&token,
&slack::channels::HistoryRequest {
channel: &env::args()
&slack::conversations::HistoryRequest {
channel: env::args()
.nth(1)
.ok_or("must specify channel id as argument e.g. C09123456")?,
..slack::channels::HistoryRequest::default()
..slack::conversations::HistoryRequest::default()
},
)
.await;
Expand Down
Loading

0 comments on commit 0cae326

Please sign in to comment.