Replace command parameters with values
To create a minimalistic parser, nom is used to identify and replace parameters given in the command field. For clarity the `action` field for hooks was renamed to `command`.
This commit is contained in:
parent
12c3b12c31
commit
0610fd49c9
5 changed files with 205 additions and 23 deletions
123
src/main.rs
123
src/main.rs
|
@ -2,6 +2,14 @@
|
|||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use log::{debug, info, trace, warn};
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::{tag, take_until},
|
||||
combinator::map_res,
|
||||
multi::many0,
|
||||
sequence::delimited,
|
||||
Finish, IResult,
|
||||
};
|
||||
use regex::Regex;
|
||||
use rocket::{fairing::AdHoc, get, http::Status, post, routes, Response, State};
|
||||
use rocket_contrib::json::Json;
|
||||
|
@ -16,7 +24,7 @@ struct Config {
|
|||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Hook {
|
||||
action: Option<String>,
|
||||
command: Option<String>,
|
||||
secrets: Vec<String>,
|
||||
filters: HashMap<String, Filter>,
|
||||
}
|
||||
|
@ -35,7 +43,34 @@ fn index() -> &'static str {
|
|||
"Hello, webhookey!"
|
||||
}
|
||||
|
||||
fn execute_hook(name: &str, hook: &Hook, data: &serde_json::Value) -> Result<()> {
|
||||
fn replace_parameter(input: &str, data: &serde_json::Value) -> Result<String> {
|
||||
let parse: IResult<&str, Vec<&str>> = many0(alt((
|
||||
map_res(
|
||||
delimited(tag("{{"), take_until("}}"), tag("}}")),
|
||||
|param: &str| {
|
||||
if let Some(value) = data.pointer(param.trim()) {
|
||||
if let Some(value) = value.as_str() {
|
||||
Ok(value)
|
||||
} else {
|
||||
bail!("Could not convert field `{}` to string", param.trim());
|
||||
}
|
||||
} else {
|
||||
bail!("Could not find `{}` in received data", param.trim());
|
||||
}
|
||||
},
|
||||
),
|
||||
take_until("{{"),
|
||||
)))(input);
|
||||
|
||||
let (last, mut result) = parse
|
||||
.finish()
|
||||
.map_err(|e| anyhow!("Could not parse command: {}", e))?;
|
||||
result.push(last);
|
||||
|
||||
Ok(result.join(""))
|
||||
}
|
||||
|
||||
fn execute_hook<'a>(name: &'a str, hook: &'a Hook, data: &'a serde_json::Value) -> Result<()> {
|
||||
debug!("Running hook `{}`", name);
|
||||
|
||||
for (filter_name, filter) in hook.filters.iter() {
|
||||
|
@ -59,25 +94,27 @@ fn execute_hook(name: &str, hook: &Hook, data: &serde_json::Value) -> Result<()>
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(action) = &hook.action {
|
||||
info!("Execute `{}` from hook `{}`", action, name);
|
||||
if let Some(command) = &hook.command {
|
||||
let command = replace_parameter(&command, data)?;
|
||||
|
||||
let action = action.split(' ').collect::<Vec<&str>>();
|
||||
info!("Execute `{}` from hook `{}`", command, name);
|
||||
|
||||
let command = Command::new(action[0]).args(&action[1..]).output()?;
|
||||
let command = command.split(' ').collect::<Vec<&str>>();
|
||||
|
||||
let exec_command = Command::new(&command[0]).args(&command[1..]).output()?;
|
||||
|
||||
info!(
|
||||
"Command `{}` exited with return code: {}",
|
||||
action[0], command.status
|
||||
&command[0], &exec_command.status
|
||||
);
|
||||
debug!(
|
||||
"Output of command `{}` on stderr: {:?}",
|
||||
action[0], &command.stderr
|
||||
&command[0], &exec_command.stderr
|
||||
);
|
||||
trace!(
|
||||
"Output of command `{}` on stdout: {:?}",
|
||||
action[0],
|
||||
&command.stdout
|
||||
&command[0],
|
||||
&exec_command.stdout
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,7 +171,7 @@ fn get_config() -> Result<File> {
|
|||
if let Ok(config) = File::open(&path) {
|
||||
info!(
|
||||
"Loading configuration from `{}`",
|
||||
path.to_str().unwrap_or("path not printable"),
|
||||
path.to_str().unwrap_or("<path unprintable>"),
|
||||
);
|
||||
|
||||
return Ok(config);
|
||||
|
@ -171,6 +208,7 @@ fn main() -> Result<()> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use rocket::{http::ContentType, local::Client};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn index() {
|
||||
|
@ -189,7 +227,7 @@ mod tests {
|
|||
hooks.insert(
|
||||
"test_hook".to_string(),
|
||||
Hook {
|
||||
action: None,
|
||||
command: None,
|
||||
secrets: vec!["valid".to_string()],
|
||||
filters: HashMap::new(),
|
||||
},
|
||||
|
@ -239,4 +277,65 @@ mod tests {
|
|||
|
||||
assert_eq!(response.status(), Status::BadRequest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_command() {
|
||||
assert_eq!(
|
||||
replace_parameter("command", &serde_json::Value::Null).unwrap(),
|
||||
"command"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter(" command", &serde_json::Value::Null).unwrap(),
|
||||
" command"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter("command ", &serde_json::Value::Null).unwrap(),
|
||||
"command "
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter(" command ", &serde_json::Value::Null).unwrap(),
|
||||
" command "
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter("command command ", &serde_json::Value::Null).unwrap(),
|
||||
"command command "
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter("{{ /foo }} command", &json!({ "foo": "bar" })).unwrap(),
|
||||
"bar command"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter(" command {{ /foo }} ", &json!({ "foo": "bar" })).unwrap(),
|
||||
" command bar "
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter(
|
||||
"{{ /foo }} command{{/field1/foo}}",
|
||||
&json!({ "foo": "bar", "field1": { "foo": "baz" } })
|
||||
)
|
||||
.unwrap(),
|
||||
"bar commandbaz"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter(" command {{ /foo }} ", &json!({ "foo": "bar" })).unwrap(),
|
||||
" command bar "
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
replace_parameter(
|
||||
" {{ /field1/foo }} command",
|
||||
&json!({ "field1": { "foo": "bar" } })
|
||||
)
|
||||
.unwrap(),
|
||||
" bar command"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue