Skip to content

Commit

Permalink
[update] conversation reset at current step if csml code change
Browse files Browse the repository at this point in the history
  • Loading branch information
amerelo committed Jan 19, 2021
1 parent 0330eed commit da98700
Show file tree
Hide file tree
Showing 34 changed files with 722 additions and 53 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions bindings/aws_lambda/src/routes/create_bot_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use csml_engine::{create_bot_version};
use csml_interpreter::data::csml_bot::CsmlBot;
use crate::format_response;

use lambda_runtime::error::HandlerError;

pub fn handler(bot: CsmlBot) -> Result<serde_json::Value, HandlerError> {

let res = create_bot_version(bot);

match res {
Ok(data) => {
Ok(serde_json::json!(
{
"isBase64Encoded": false,
"statusCode": 201,
"headers": { "Content-Type": "application/json" },
"body": data,
}
))
},
Err(err) => {
let error = format!("EngineError: {:?}", err);
return Ok(format_response(400, serde_json::json!(error)))
}
}
}
25 changes: 25 additions & 0 deletions bindings/aws_lambda/src/routes/get_bot_by_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use csml_engine::{get_bot_by_version_id };
use crate::format_response;

use crate::{routes::GetByIdRequest};
use lambda_runtime::error::HandlerError;

pub fn handler(body: GetByIdRequest) -> Result<serde_json::Value, HandlerError> {

let res = get_bot_by_version_id(&body.id, &body.bot_id);

match res {
Ok(data) => Ok(serde_json::json!(
{
"isBase64Encoded": false,
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": serde_json::json!(data).to_string()
}
)),
Err(err) => {
let error = format!("EngineError: {:?}", err);
return Ok(format_response(400, serde_json::json!(error)))
}
}
}
39 changes: 39 additions & 0 deletions bindings/aws_lambda/src/routes/get_bot_by_version_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use csml_engine::{get_bot_by_version_id };
use crate::format_response;

use crate::{routes::GetByIdRequest};
use lambda_runtime::error::HandlerError;

pub fn handler(body: GetByIdRequest) -> Result<serde_json::Value, HandlerError> {

let res = get_bot_by_version_id(&body.id, &body.bot_id);

match res {
Ok(data) => {
match data {
Some(data) => {
Ok(serde_json::json!(
{
"isBase64Encoded": false,
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": data.flatten()
}
))
}
None => {
Ok(serde_json::json!({
"isBase64Encoded": false,
"statusCode": 400,
"headers": { "Content-Type": "application/json" },
"body": "Not found"
}))
}
}
},
Err(err) => {
let error = format!("EngineError: {:?}", err);
return Ok(format_response(400, serde_json::json!(error)))
}
}
}
26 changes: 26 additions & 0 deletions bindings/aws_lambda/src/routes/get_bot_versions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use csml_engine::{get_bot_versions };
use crate::format_response;

use lambda_runtime::error::HandlerError;
use crate::{routes::GetVersionsRequest};


pub fn handler(body: GetVersionsRequest) -> Result<serde_json::Value, HandlerError> {

let res = get_bot_versions(&body.bot_id, body.limit, body.last_key);

match res {
Ok(data) => Ok(serde_json::json!(
{
"isBase64Encoded": false,
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": data
}
)),
Err(err) => {
let error = format!("EngineError: {:?}", err);
return Ok(format_response(400, serde_json::json!(error)))
}
}
}
39 changes: 39 additions & 0 deletions bindings/aws_lambda/src/routes/get_last_bot_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use csml_engine::{get_last_bot_version};
use crate::format_response;

use lambda_runtime::error::HandlerError;

pub fn handler(bot_id: String) -> Result<serde_json::Value, HandlerError> {

let res = get_last_bot_version(&bot_id);

match res {
Ok(data) => {
match data {
Some(data) => {
Ok(serde_json::json!(
{
"isBase64Encoded": false,
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": data.flatten()
}
))
}
None => {
Ok(serde_json::json!({
"isBase64Encoded": false,
"statusCode": 400,
"headers": { "Content-Type": "application/json" },
"body": "Not found"
}))
}
}

},
Err(err) => {
let error = format!("EngineError: {:?}", err);
return Ok(format_response(400, serde_json::json!(error)))
}
}
}
6 changes: 6 additions & 0 deletions csml_engine/CSML/flow.csml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ fn add(var1, var2):
return 56

start:
say "hello World"
// goto end
goto toto

toto:
do value = "value = {{ pow(2, 5) }}"
say value
hold
say plop(1, 2)

goto end
1 change: 1 addition & 0 deletions csml_engine/examples/command_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ fn init_bot() -> CsmlBot {
native_components: Some(load_components().unwrap()),
custom_components: None,
default_flow: "flowid".to_owned(),
bot_ast: None,
}
}

Expand Down
41 changes: 37 additions & 4 deletions csml_engine/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use crate::db_connectors::{conversations::*, interactions::*, memories::*};
use crate::{
data::{ConversationInfo, CsmlRequest, Database, EngineError},
utils::{get_default_flow, get_flow_by_id, search_flow},
Context, CsmlBot, CsmlFlow,
Context, CsmlBot, CsmlFlow, CsmlResult
};

use csml_interpreter::data::{
context::{get_hashmap_from_json, get_hashmap_from_mem},
ApiInfo, Client, Event,
use csml_interpreter::{
data::{
context::{get_hashmap_from_json, get_hashmap_from_mem},
ApiInfo, Client, Event,
},
validate_bot, load_components
};
use curl::{
easy::{Easy, List},
Expand Down Expand Up @@ -96,6 +99,36 @@ pub fn init_conversation_info<'a>(
Ok(data)
}

/**
* Initialize the bot
*/
pub fn init_bot(bot: &mut CsmlBot) -> Result<(), EngineError> {
// load native components into the bot
bot.native_components = match load_components() {
Ok(components) => Some(components),
Err(err) => return Err(EngineError::Interpreter(err.format_error())),
};

match validate_bot(&bot) {
CsmlResult {
flows: Some(flows),
errors: None,
..
} => {
bot.bot_ast = Some(base64::encode(bincode::serialize(&flows).unwrap()));
Ok(())
}
CsmlResult {
errors: Some(errors),
..
} => Err(EngineError::Interpreter(format!(
"invalid bot {:?}",
errors
))),
_ => Err(EngineError::Interpreter(format!("empty bot"))),
}
}

/**
* Initialize the context object for incoming requests
*/
Expand Down
11 changes: 5 additions & 6 deletions csml_engine/src/interpreter_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use csml_interpreter::{
data::{csml_bot::CsmlBot, csml_flow::CsmlFlow, Event, Hold, MSG},
interpret,
};
use md5::{Digest, Md5};
// use md5::{Digest, Md5};
use serde_json::{map::Map, Value};
use std::{env, sync::mpsc, thread, time::SystemTime};

Expand Down Expand Up @@ -47,20 +47,19 @@ pub fn interpret_step(
MSG::Hold(Hold {
index: new_index,
step_vars,
step_hash,
}) => {
let mut hash = Md5::new();

hash.update(current_flow.content.as_bytes());

let state_hold: Value = serde_json::json!({
"index": new_index,
"step_vars": step_vars,
"hash": format!("{:x}", hash.finalize())
"step_hash": serde_json::to_value(&step_hash)?
});

set_state_items(data, "hold", vec![("position", &state_hold)])?;
data.context.hold = Some(Hold {
index: new_index,
step_vars,
step_hash,
});
}
MSG::Next { flow, step } => match (flow, step) {
Expand Down
59 changes: 44 additions & 15 deletions csml_engine/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
pub mod data;
pub use csml_interpreter::data::{
csml_result::CsmlResult, error_info::ErrorInfo, warnings::Warnings, Client,
ast::{Expr, Flow, InstructionScope},
error_info::ErrorInfo,
warnings::Warnings,
Client, CsmlResult,
};
use serde_json::json;

Expand Down Expand Up @@ -48,6 +51,7 @@ pub fn start_conversation(
let formatted_event = format_event(json!(request))?;
let mut db = init_db()?;
let mut bot = run_opt.search_bot(&mut db);
init_bot(&mut bot)?;

// load native components into the bot
bot.native_components = match load_components() {
Expand All @@ -68,7 +72,7 @@ pub fn start_conversation(
add_messages_bulk(&mut data, msgs, 0, "RECEIVE")?;

let flow = get_flow_by_id(&data.context.flow, &bot.flows)?;
check_for_hold(&mut data, flow)?;
check_for_hold(&mut data, &bot.bot_ast, flow)?;

let res = interpret_step(&mut data, formatted_event.to_owned(), &bot);

Expand Down Expand Up @@ -190,7 +194,7 @@ pub fn get_steps_from_flow(bot: CsmlBot) -> HashMap<String, Vec<String>> {
* (missing steps or flows, syntax errors, etc.)
*/
pub fn validate_bot(bot: CsmlBot) -> CsmlResult {
csml_interpreter::validate_bot(bot)
csml_interpreter::validate_bot(&bot)
}

/**
Expand All @@ -215,23 +219,47 @@ pub fn user_close_all_conversations(client: Client) -> Result<(), EngineError> {
* If the hold is valid, we also need to load the local step memory
* (context.hold.step_vars) into the conversation context.
*/
fn check_for_hold(data: &mut ConversationInfo, flow: &CsmlFlow) -> Result<(), EngineError> {
fn check_for_hold(
data: &mut ConversationInfo,
bot_ast: &Option<String>,
flow: &CsmlFlow,
) -> Result<(), EngineError> {
match get_state_key(&data.client, "hold", "position", &mut data.db) {
// user is currently on hold
Ok(Some(string)) => {
let hold = serde_json::to_value(string)?;
let mut hash = Md5::new();

hash.update(flow.content.as_bytes());
let new_hash = format!("{:x}", hash.finalize());

// cleanup the current hold and restart flow
if new_hash != hold["hash"] {
data.context.step = "start".to_owned();
delete_state_key(&data.client, "hold", "position", &mut data.db)?;
data.context.hold = None;
return Ok(());
}
let step_hash = match (hold.get("hash"), hold.get("step_hash")) {
// this block is only for retro compatibility whit the old method
(Some(old_hash), None) => {
let mut hash = Md5::new();

hash.update(flow.content.as_bytes());
let new_hash = format!("{:x}", hash.finalize());

// cleanup the current hold and restart flow
if new_hash != *old_hash {
data.context.step = "start".to_owned();
delete_state_key(&data.client, "hold", "position", &mut data.db)?;
data.context.hold = None;
return Ok(());
}
new_hash
}
(None, Some(step_hash_value)) => {
// cleanup the current hold and restart flow
let step_hash = match get_current_step_hash(bot_ast, data) {
Ok(step_hash) if step_hash != *step_hash_value => {
return clean_hold_and_restart(data)
}
Ok(step_hash) => step_hash,
Err(_) => return clean_hold_and_restart(data),
};

step_hash
}
_ => return Ok(()),
};

// all good, let's load the position and local variables
data.context.hold = Some(Hold {
Expand All @@ -240,6 +268,7 @@ fn check_for_hold(data: &mut ConversationInfo, flow: &CsmlFlow) -> Result<(), En
.ok_or(EngineError::Interpreter("hold index bad format".to_owned()))?
as usize,
step_vars: hold["step_vars"].clone(),
step_hash: step_hash,
});
delete_state_key(&data.client, "hold", "position", &mut data.db)?;
}
Expand Down
Loading

0 comments on commit da98700

Please sign in to comment.