Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hidden match property handling #1

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions filmreel/src/cut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl Register {
/// use in cut operations.
///
/// [Read Operation](https://github.com/Bestowinc/filmReel/blob/master/cut.md#read-operation)
pub fn read_match(&self, json_string: &str) -> Result<Vec<Match>, FrError> {
pub fn read_match<T: AsRef<str>>(&self, json_string: T) -> Result<Vec<Match>, FrError> {
lazy_static! {
static ref VAR_MATCH: Regex = Regex::new(
r"(?x)
Expand All @@ -134,7 +134,7 @@ impl Register {

let mut matches: Vec<Match> = Vec::new();

for mat in VAR_MATCH.captures_iter(json_string) {
for mat in VAR_MATCH.captures_iter(json_string.as_ref()) {
// continue if the leading brace is escaped but strip "\\" from the match
if let Some(esc_char) = mat.name("esc_char") {
matches.push(Match::Escape(esc_char.range().clone()));
Expand Down Expand Up @@ -373,32 +373,30 @@ impl<'a> Match<'a> {
value: match_val,
range: r,
..
} => match match_val {
// if the match value is a string
Value::String(match_str) => match json_value {
// and the json value is as well, replace the range within
Value::String(str_val) => {
} => {
if let Value::String(match_str) = match_val {
// if both match_val AND json_value are strings,
// replace the range within
if let Value::String(str_val) = json_value {
str_val.replace_range(r, &match_str);
Ok(())
return Ok(());
}
_ => Err(FrError::ReadInstruction(
return Err(FrError::ReadInstruction(
"Match::Variable given a non string value to replace",
)),
},
_ => {
*json_value = match_val.clone();
Ok(())
));
}
},
Match::Hide => match json_value {
Value::String(json_str) => {
*json_value = match_val.clone();
Ok(())
}
Match::Hide => {
if let Value::String(json_str) = json_value {
*json_str = "${_HIDDEN}".to_string();
Ok(())
return Ok(());
}
_ => Err(FrError::ReadInstruction(
Err(FrError::ReadInstruction(
"Match::Hide.value is a non string Value",
)),
},
))
}
}
}
}
Expand Down
111 changes: 59 additions & 52 deletions filmreel/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
cut::Register,
error::FrError,
response::Response,
utils::{ordered_set, ordered_str_map},
utils::{ordered_set, ordered_str_map, MatchQuery},
};
use serde::{Deserialize, Serialize};
use serde_json::{error::Error as SerdeError, json, to_value, Value};
Expand Down Expand Up @@ -49,10 +49,8 @@ impl<'a> Frame<'a> {
}

/// Serialized payload
pub fn get_request_uri(&self) -> Result<String, FrError> {
let unst = serde_json::to_string(&self.request.uri)?;

Ok(unst.replace("\"", ""))
pub fn get_request_uri(&self) -> String {
self.request.get_uri()
}

/// Returns a Value object from the response body, used for response comparisons and writing to
Expand All @@ -63,27 +61,31 @@ impl<'a> Frame<'a> {

/// Traverses Frame properties where Read Operations are permitted and
/// performs Register.read_operation on Strings with Cut Variables
pub fn hydrate(&mut self, reg: &Register, hide: bool) -> Result<(), FrError> {
pub fn hydrate(&mut self, reg: &Register, hide: bool) -> Result<Vec<MatchQuery>, FrError> {
let set = self.cut.clone();
if let Some(request_body) = &mut self.request.body {
Self::hydrate_val(&set, request_body, reg, hide)?;
}
if let Some(response_body) = &mut self.response.body {
Self::hydrate_val(&set, response_body, reg, hide)?;
}
if let Some(header) = &mut self.request.header {
Self::hydrate_val(&set, header, reg, hide)?;
}
if let Some(etc) = &mut self.request.etc {
Self::hydrate_val(&set, etc, reg, hide)?;
}

// URI and entrypoint is given an explicit read operation
Self::hydrate_str(&set, &mut self.request.uri, reg, hide)?;
if let Some(entrypoint) = &mut self.request.entrypoint {
Self::hydrate_str(&set, entrypoint, reg, hide)?;
// call Frame::hydrate_val on all properties that are Option::Some
let match_selectors: Vec<MatchQuery> = vec![
&mut self.request.body,
&mut self.request.header,
&mut self.request.etc,
&mut self.request.entrypoint,
&mut self.response.body,
&mut self.response.etc,
]
.into_iter()
.filter_map(|val| val.as_mut())
.map(|val| Self::hydrate_val(&set, val, reg, hide, MatchQuery::new()))
.collect::<Result<Vec<_>, FrError>>()?;

// cast uri to serde_json::Value so that we can use it in hydrate_str
let mut value_uri = Value::String(self.request.uri.to_owned());
Self::hydrate_str(&set, &mut value_uri, reg, hide, MatchQuery::new())?;
if let Value::String(s) = value_uri {
self.request.uri = s;
}
Ok(())

Ok(match_selectors)
}

/// Traverses a given serde::Value enum attempting to modify found Strings
Expand All @@ -93,26 +95,29 @@ impl<'a> Frame<'a> {
val: &mut Value,
reg: &Register,
hide: bool,
) -> Result<(), FrError> {
match val {
Value::Object(map) => {
for (_, val) in map.iter_mut() {
Self::hydrate_val(set, val, reg, hide)?;
}
Ok(())
}
Value::Array(vec) => {
for val in vec.iter_mut() {
Self::hydrate_val(set, val, reg, hide)?;
}
Ok(())
}
Value::String(_) => {
Self::hydrate_str(set, val, reg, hide)?;
Ok(())
}
_ => Ok(()),
match_selector: MatchQuery,
) -> Result<Vec<MatchQuery>, FrError> {
let selectors = match val {
Value::Object(map) => map
.iter_mut()
.map(|(k, val)| {
let k_query = match_selector.clone();
k_query.append(k);
Self::hydrate_val(set, val, reg, hide, k_query)
})
.collect(),
Value::Array(vec) => vec.iter_mut().enumerate().map(|(i, val)| {
let i_query = match_selector.clone();
i_query.append(i);
Self::hydrate_val(set, val, reg, hide, match_selector)
}),
Value::String(_) => Self::hydrate_str(set, val, reg, hide, match_selector)?,
_ => None,
}
.filter(Option::is_some)
.collect();

Ok(selectors)
}

/// Performs a Register.read_operation on the entire String
Expand All @@ -121,9 +126,14 @@ impl<'a> Frame<'a> {
string: &mut Value,
reg: &Register,
hide: bool,
) -> Result<(), FrError> {
match_selector: MatchQuery,
) -> Result<Option<MatchQuery>, FrError> {
{
let matches = reg.read_match(&string.as_str().expect("hydrate_str None found"))?;
let matches = reg.read_match(
&string
.as_str()
.ok_or_else(|| FrError::ReadInstruction("hydrate_str None found"))?,
)?;
// Check if the InstructionSet has the given variable
for mat in matches.into_iter() {
if let Some(n) = mat.name() {
Expand All @@ -143,7 +153,7 @@ impl<'a> Frame<'a> {
}
}
}
Ok(())
Ok(None)
}
}
}
Expand Down Expand Up @@ -225,7 +235,7 @@ impl<'a> InstructionSet<'a> {
pub struct Request {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) body: Option<Value>,
pub(crate) uri: Value,
pub(crate) uri: String,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub(crate) etc: Option<Value>,

Expand All @@ -245,10 +255,7 @@ impl Request {
}

pub fn get_uri(&self) -> String {
if let Value::String(string) = &self.uri {
return string.to_string();
}
"".to_string()
self.uri.clone()
}

pub fn get_etc(&self) -> Option<Value> {
Expand All @@ -271,7 +278,7 @@ impl Default for Request {
fn default() -> Self {
Self {
body: None,
uri: Value::Null,
uri: String::new(),
etc: Some(json!({})),
header: None,
entrypoint: None,
Expand Down Expand Up @@ -407,7 +414,7 @@ mod tests {
})),
header: Some(json!({"Authorization": "Bearer jWt"})),
entrypoint: Some(json!("localhost:8080")),
uri: json!("user_api.User/CreateUser"),
uri: "user_api.User/CreateUser".to_string(),
etc: Some(json!({})),
},

Expand Down
8 changes: 6 additions & 2 deletions filmreel/src/reel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ impl Reel {
pub fn success_only(self) -> Self {
Self {
dir: self.dir,
frames: self.frames.into_iter().filter(|x| x.is_success()).collect(),
frames: self
.frames
.into_iter()
.filter(MetaFrame::is_success)
.collect(),
}
}

Expand Down Expand Up @@ -107,7 +111,7 @@ impl Reel {

for entry in glob(dir_glob.as_ref().to_str().unwrap())
.map_err(|e| FrError::ReelParsef("PatternError: {}", e.to_string()))?
.filter_map(|r| r.ok())
.filter_map(Result::ok)
.filter(|path| path.is_file())
{
let frame = MetaFrame::try_from(&entry)?;
Expand Down
8 changes: 4 additions & 4 deletions filmreel/src/serde_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ test_ser_de!(
request,
Request {
body: Some(json!({"email": "[email protected]"})),
uri: json!("user_api.User/CreateUser"),
uri: "user_api.User/CreateUser".to_string(),
..Default::default()
},
REQUEST_JSON
Expand All @@ -78,7 +78,7 @@ test_ser_de!(
header: Some(json!({"Authorization": "${USER_TOKEN}"})),
entrypoint: None,
etc: Some(json!({"id": "007"})),
uri: json!("POST /logout/${USER_ID}"),
uri: "POST /logout/${USER_ID}".to_string(),
},
REQUEST_ETC_JSON
);
Expand Down Expand Up @@ -187,7 +187,7 @@ test_ser_de!(
request: Request {
body: Some(json!({})),
header: Some(json!({ "Authorization": "${USER_TOKEN}" })),
uri: json!("POST /logout/${USER_ID}"),
uri: "POST /logout/${USER_ID}".to_string(),
..Default::default()
},

Expand Down Expand Up @@ -220,7 +220,7 @@ test_ser_de!(
protocol: Protocol::HTTP,
cut: InstructionSet::default(),
request: Request {
uri: json!("POST /logout/${USER_ID}"),
uri: "POST /logout/${USER_ID}".to_string(),
..Default::default()
},

Expand Down
38 changes: 38 additions & 0 deletions filmreel/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,44 @@ pub fn new_selector(query: &str) -> Result<Selector, FrError> {
Ok(selector_fn)
}

#[derive(Clone)]
pub(crate) struct MatchQuery {
pub(crate) query: String,
// kind: crate::cut::Match<'a>,
}

impl MatchQuery {
pub(crate) fn new() -> Self {
Self {
query: String::new(),
}
}
pub(crate) fn append<T: Into<MatchIndex>>(&mut self, i: T) {
self.query.push('.');
self.query.push_str(i.into().to_str());
}
}

pub struct MatchIndex(String);

impl MatchIndex {
fn to_str(&self) -> &str {
self.to_str()
}
}

impl From<&String> for MatchIndex {
fn from(i: &String) -> Self {
MatchIndex(format!("'{}'", i.to_string()))
}
}

impl From<usize> for MatchIndex {
fn from(i: usize) -> Self {
MatchIndex(format!("[{}]", i.to_string()))
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion src/take.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ pub fn run_take<'a>(
let _ = stdin.read(&mut [0u8]).expect("read stdin panic");
} else if verbose {
let hidden = hidden_frame.ok_or_else(|| anyhow!("None for interactive hidden_frame"))?;
info!("{} {}", "Request URI:".yellow(), frame.get_request_uri()?);
info!("{} {}", "Request URI:".yellow(), frame.get_request_uri());
info!("[{}] frame:", "Hydrated".green());
info!("{}", hidden.to_coloured_tk_json()?);
}
Expand Down