Skip to content

Commit

Permalink
add inline scripting feature for accessing out yaml tree
Browse files Browse the repository at this point in the history
It is now possible to evaluate a property in the user's yaml by referencing a property in the processing output yaml.

The format for referencing a path in the output yaml is:

- {{ .property }}
- {{ .property[n] }}

And all the arbitrary compositions of the above base cases.
An abbreviated format is also possible:

{{ .[n][m].property }}

This is equivalent to:

{{ .conversations[n].requests[m].property }}

Example:

conversations:
    host: localhost:7480
    requests:
      - method: POST
        uri: test-5/mp.3
        queryString: format=json&uploads
      - method: PUT
        uri: test-5/mp.3
        queryString: format=json&partNumber=1&uploadId={{.[0][0].response.body.UploadId}}

The queryString property in the second request is evaluated referencing the 'response.body.UploadId' path starting from the .conversations[0].requests[0] node in the output yaml.

Signed-off-by: Giuseppe Baccini <giuseppe.baccini@suse.com>
  • Loading branch information
Giuseppe Baccini committed Oct 19, 2023
1 parent 2c31b9e commit 4208bfc
Show file tree
Hide file tree
Showing 14 changed files with 675 additions and 52 deletions.
4 changes: 2 additions & 2 deletions src/.astylerc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
--indent-switches
--indent-col1-comments
--min-conditional-indent=2
--max-instatement-indent=100
--add-brackets
--max-continuation-indent=100
--add-braces
--lineend=linux
--convert-tabs
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ project(chatterbox_binary VERSION 0.0.0)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wall -std=c++17)
# add_compile_options(-g -Wall -std=c++17)
endif()

add_definitions(-DV8_COMPRESS_POINTERS
Expand Down
128 changes: 116 additions & 12 deletions src/conversation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ void conversation::statistics::incr_categorization(const std::string &key)

conversation::conversation(scenario &parent) :
parent_(parent),
scen_out_p_resolv_(parent_.scen_out_p_resolv_),
scen_p_evaluator_(parent_.scen_p_evaluator_),
js_env_(parent.js_env_),
stats_(*this),
event_log_(parent.event_log_)
Expand Down Expand Up @@ -71,56 +73,158 @@ int conversation::process(ryml::NodeRef conversation_in,

if(scope.enabled_) {
parent_.stats_.incr_conversation_count();

auto raw_host = js_env_.eval_as<std::string>(conversation_out, key_host);
bool further_eval = false;
auto raw_host = js_env_.eval_as<std::string>(conversation_out,
key_host,
std::nullopt,
true,
nullptr,
PROP_EVAL_RGX,
&further_eval);
if(!raw_host) {
event_log_->error(ERR_FAIL_READ_HOST);
return 1;
} else {
conversation_out.remove_child(key_host);
conversation_out[key_host] << *raw_host;
if(further_eval) {
raw_host = scen_p_evaluator_.eval_as<std::string>(conversation_out,
key_host,
scen_out_p_resolv_);
}
if(!raw_host) {
event_log_->error(ERR_FAIL_READ_HOST);
utils::clear_map_node_put_key_val(conversation_out, key_error, ERR_FAIL_READ_HOST);
return 1;
}
}

conversation_out.remove_child(key_host);
conversation_out[key_host] << *raw_host;

raw_host_ = *raw_host;
auto host = raw_host_;
utils::find_and_replace(host, "http://", "");
utils::find_and_replace(host, "https://", "");
host = host.substr(0, (host.find(':') == std::string::npos ? host.length() : host.find(':')));

//auth
further_eval = false;
if(conversation_out.has_child(key_auth)) {
ryml::NodeRef auth_node = conversation_out[key_auth];
auto service = js_env_.eval_as<std::string>(auth_node, key_service, "s3");
auto service = js_env_.eval_as<std::string>(auth_node,
key_service,
"s3",
true,
nullptr,
PROP_EVAL_RGX,
&further_eval);

if(further_eval) {
service = scen_p_evaluator_.eval_as<std::string>(conversation_out,
key_service,
scen_out_p_resolv_);
if(!service) {
event_log_->error(ERR_FAIL_EVAL);
utils::clear_map_node_put_key_val(conversation_out, key_error, ERR_FAIL_EVAL);
return 1;
}
}
if(service) {
if(auth_node.has_child(key_service)) {
auth_node.remove_child(key_service);
}
auth_node[key_service] << *service;
}

auto access_key = js_env_.eval_as<std::string>(auth_node, key_access_key);
//access_key
further_eval = false;
auto access_key = js_env_.eval_as<std::string>(auth_node,
key_access_key,
std::nullopt,
true,
nullptr,
PROP_EVAL_RGX,
&further_eval);
if(further_eval) {
access_key = scen_p_evaluator_.eval_as<std::string>(conversation_out,
key_access_key,
scen_out_p_resolv_);
if(!access_key) {
event_log_->error(ERR_FAIL_EVAL);
utils::clear_map_node_put_key_val(conversation_out, key_error, ERR_FAIL_EVAL);
return 1;
}
}
if(access_key) {
auth_node.remove_child(key_access_key);
auth_node[key_access_key] << *access_key;
}

auto secret_key = js_env_.eval_as<std::string>(auth_node, key_secret_key);
//secret_key
further_eval = false;
auto secret_key = js_env_.eval_as<std::string>(auth_node,
key_secret_key,
std::nullopt,
true,
nullptr,
PROP_EVAL_RGX,
&further_eval);
if(further_eval) {
secret_key = scen_p_evaluator_.eval_as<std::string>(conversation_out,
key_secret_key,
scen_out_p_resolv_);
if(!secret_key) {
event_log_->error(ERR_FAIL_EVAL);
utils::clear_map_node_put_key_val(conversation_out, key_error, ERR_FAIL_EVAL);
return 1;
}
}
if(secret_key) {
auth_node.remove_child(key_secret_key);
auth_node[key_secret_key] << *secret_key;
}

//signed_headers
further_eval = false;
auto signed_headers = js_env_.eval_as<std::string>(auth_node,
key_signed_headers,
AUTH_AWS_DEF_SIGN_HDRS);
AUTH_AWS_DEF_SIGN_HDRS,
true,
nullptr,
PROP_EVAL_RGX,
&further_eval);
if(further_eval) {
signed_headers = scen_p_evaluator_.eval_as<std::string>(conversation_out,
key_signed_headers,
scen_out_p_resolv_);
if(!signed_headers) {
event_log_->error(ERR_FAIL_EVAL);
utils::clear_map_node_put_key_val(conversation_out, key_error, ERR_FAIL_EVAL);
return 1;
}
}
if(signed_headers) {
if(auth_node.has_child(key_signed_headers)) {
auth_node.remove_child(key_signed_headers);
}
auth_node[key_signed_headers] << *signed_headers;
}

auto region = js_env_.eval_as<std::string>(auth_node, key_region, "US");
//region
auto region = js_env_.eval_as<std::string>(auth_node,
key_region,
"US",
true,
nullptr,
PROP_EVAL_RGX,
&further_eval);

if(further_eval) {
region = scen_p_evaluator_.eval_as<std::string>(conversation_out,
key_region,
scen_out_p_resolv_);
if(!region) {
event_log_->error(ERR_FAIL_EVAL);
utils::clear_map_node_put_key_val(conversation_out, key_error, ERR_FAIL_EVAL);
return 1;
}
}
if(region) {
if(auth_node.has_child(key_region)) {
auth_node.remove_child(key_region);
Expand Down
6 changes: 6 additions & 0 deletions src/conversation.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ struct conversation {
//parent
scenario &parent_;

//scenario property resolver
scenario_property_resolver &scen_out_p_resolv_;

//scenario property evaluator
scenario_property_evaluator &scen_p_evaluator_;

//js environment
js::js_env &js_env_;

Expand Down
1 change: 0 additions & 1 deletion src/jsenv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ int js_env::reset()
v8::NewStringType::kNormal).ToLocalChecked(),
v8::FunctionTemplate::New(isolate_, cbk_log));


//bind cbk_load.
global->Set(v8::String::NewFromUtf8(isolate_,
"load",
Expand Down
30 changes: 18 additions & 12 deletions src/jsenv.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ struct converter<bool> {
if(!val.is_keyval() && !val.is_val()) {
return false;
}
bool bval;
val >> bval;
//@todo
return true;
}
static bool isType(const v8::Local<v8::Value> &val) {
Expand All @@ -44,8 +43,7 @@ struct converter<int32_t> {
if(!val.is_keyval() && !val.is_val()) {
return false;
}
int32_t ival;
val >> ival;
//@todo
return true;
}
static bool isType(const v8::Local<v8::Value> &val) {
Expand All @@ -70,8 +68,7 @@ struct converter<uint32_t> {
if(!val.is_keyval() && !val.is_val()) {
return false;
}
uint32_t uival;
val >> uival;
//@todo
return true;
}
static bool isType(const v8::Local<v8::Value> &val) {
Expand All @@ -96,8 +93,7 @@ struct converter<double> {
if(!val.is_keyval() && !val.is_val()) {
return false;
}
double dval;
val >> dval;
//@todo
return true;
}
static bool isType(const v8::Local<v8::Value> &val) {
Expand All @@ -122,8 +118,7 @@ struct converter<std::string> {
if(!val.is_keyval() && !val.is_val()) {
return false;
}
std::string sval;
val >> sval;
//@todo
return true;
}
static bool isType(const v8::Local<v8::Value> &val) {
Expand Down Expand Up @@ -219,19 +214,30 @@ struct js_env {
static void cbk_assert(const v8::FunctionCallbackInfo<v8::Value> &args);

// ----------------------------
// --- Json Field Evaluator ---
// --- Yaml Field Evaluator ---
// ----------------------------

template <typename T>
std::optional<T> eval_as(ryml::NodeRef from,
const char *key,
const std::optional<T> default_value = std::nullopt,
bool log_errors = true,
bool *is_error = nullptr) {
bool *is_error = nullptr,
const char *check_regex = nullptr,
bool *further_eval = nullptr) {
if(!from.has_child(ryml::to_csubstr(key))) {
return default_value;
}
ryml::NodeRef val = from[ryml::to_csubstr(key)];

if(check_regex && (val.is_keyval() || val.is_val())) {
auto str_val = utils::converter<std::string>::asType(val);
if(std::regex_search(utils::converter<std::string>::asType(val), std::regex(check_regex))) {
*further_eval = true;
return std::nullopt;
}
}

if(utils::converter<T>::isType(val)) {
//eval as primitive value
return utils::converter<T>::asType(val);
Expand Down
Loading

0 comments on commit 4208bfc

Please sign in to comment.