Skip to content

Commit

Permalink
refactored node to action, extension to plugins and added config schema
Browse files Browse the repository at this point in the history
carllippert committed May 18, 2024
1 parent e08abf3 commit 4d0abca
Showing 6 changed files with 154 additions and 138 deletions.
27 changes: 23 additions & 4 deletions plugin-core/crates/pdk-mock-plugin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -15,14 +15,13 @@ pub fn execute(config: Value) -> FnResult<Value> {
Ok(config)
}


#[plugin_fn]
pub fn register() -> FnResult<Action> {
//Used to let UI and users know how to configure actions
let action: Action = Action::builder()
.trigger(false)
.node_name("example_node".to_string())
.node_label("Example Node".to_string())
.action_name("example_node".to_string())
.action_label("Example Node".to_string())
.icon("<svg></svg>".to_string())
.description("This is an example action".to_string())
.variables(vec![])
@@ -32,7 +31,27 @@ pub fn register() -> FnResult<Action> {
"headers": {},
"body": ""
}))
.extension_id("example_extension".to_string())
.config_schema(serde_json::json!({
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
},
"url": {
"type": "string"
},
"headers": {
"type": "object"
},
"body": {
"type": "string"
}
},
"required": ["method", "url"],
"additionalProperties": false
}))
.plugin_id("example_extension".to_string())
.build();

Ok(action)
20 changes: 12 additions & 8 deletions plugin-core/crates/pdk-test-runner/src/lib.rs
Original file line number Diff line number Diff line change
@@ -27,18 +27,15 @@ pub fn test() -> FnResult<()> {
xtp_test::assert!("action trigger is false", action.trigger == false);

xtp_test::assert!(
"action extension_id is non-empty",
!action.extension_id.is_empty()
"action plugin_id is non-empty",
!action.plugin_id.is_empty()
);

xtp_test::assert!(
"action node_name is non-empty",
!action.node_name.is_empty()
);
xtp_test::assert!(
"action node_label is non-empty",
!action.node_label.is_empty()
"action action_name is non-empty",
!action.action_name.is_empty()
);
xtp_test::assert!("action_label is non-empty", !action.action_label.is_empty());
xtp_test::assert!("action icon is non-empty", !action.icon.is_empty());
xtp_test::assert!(
"action description is non-empty",
@@ -51,9 +48,16 @@ pub fn test() -> FnResult<()> {
validate_config(&action.config)
);

// Validate that the config schema is real
// Validate the config field
xtp_test::assert!(
"config_schema is valid JSON and has keys",
validate_config(&action.config_schema)
);
Ok(())
}

//TODO: run integration tests with jsonschema rs in the "simple mock host"
// Function to validate the config field
fn validate_config(config: &Value) -> bool {
// Check if config is an object and contains at least one key
2 changes: 2 additions & 0 deletions plugin-core/crates/pdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -10,4 +10,6 @@ repository = "https://github.com/tryanything-ai/anything"
extism-pdk = "1.1.0"
serde = { version = "1.0.201", features = ["derive"] }
serde_json = "1.0.117"

[dev-dependencies]
jsonschema = "0.18"
176 changes: 92 additions & 84 deletions plugin-core/crates/pdk/src/action.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use extism_pdk::*;
use jsonschema::{is_valid, JSONSchema};
use serde::{Deserialize, Serialize};
use serde_json::Value;


#[derive(Serialize, Deserialize, ToBytes, Debug, PartialEq, Clone)]
#[encoding(Json)]
pub struct Handle {
@@ -163,95 +163,103 @@ impl ActionBuilder {
}
}

#[test]
fn builder_test() {
let action = Action {
trigger: false,
action_name: "example_node".to_string(),
action_label: "Example Node".to_string(),
icon: "<svg></svg>".to_string(),
description: "This is an example action".to_string(),
handles: vec![
Handle {
id: "a".to_string(),
position: "top".to_string(),
r#type: "target".to_string(),
},
Handle {
id: "b".to_string(),
position: "bottom".to_string(),
r#type: "source".to_string(),
},
],
variables: vec![],
config: serde_json::json!({
"method": "GET",
"url": "http://example.com",
"headers": {},
"body": ""
}),
config_schema: serde_json::json!({
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
#[cfg(test)]
mod tests {
use super::*;
use jsonschema::{Draft, JSONSchema};

#[test]
fn builder_test() {
use jsonschema::{is_valid, JSONSchema};
let action = Action {
trigger: false,
action_name: "example_node".to_string(),
action_label: "Example Node".to_string(),
icon: "<svg></svg>".to_string(),
description: "This is an example action".to_string(),
handles: vec![
Handle {
id: "a".to_string(),
position: "top".to_string(),
r#type: "target".to_string(),
},
"url": {
"type": "string"
Handle {
id: "b".to_string(),
position: "bottom".to_string(),
r#type: "source".to_string(),
},
"headers": {
"type": "object"
],
variables: vec![],
config: serde_json::json!({
"method": "GET",
"url": "http://example.com",
"headers": {},
"body": ""
}),
config_schema: serde_json::json!({
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
},
"url": {
"type": "string"
},
"headers": {
"type": "object"
},
"body": {
"type": "string"
}
},
"body": {
"type": "string"
}
},
"required": ["method", "url"],
"additionalProperties": false
}),
plugin_id: "example_extension".to_string(),
};
"required": ["method", "url"],
"additionalProperties": false
}),
plugin_id: "example_extension".to_string(),
};

let action_from_builder: Action = Action::builder()
.trigger(false)
.action_name("example_node".to_string())
.action_label("Example Node".to_string())
.icon("<svg></svg>".to_string())
.description("This is an example action".to_string())
.variables(vec![])
.config(serde_json::json!({
"method": "GET",
"url": "http://example.com",
"headers": {},
"body": ""
}))
.config_schema(serde_json::json!({
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
},
"url": {
"type": "string"
},
"headers": {
"type": "object"
let action_from_builder: Action = Action::builder()
.trigger(false)
.action_name("example_node".to_string())
.action_label("Example Node".to_string())
.icon("<svg></svg>".to_string())
.description("This is an example action".to_string())
.variables(vec![])
.config(serde_json::json!({
"method": "GET",
"url": "http://example.com",
"headers": {},
"body": ""
}))
.config_schema(serde_json::json!({
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
},
"url": {
"type": "string"
},
"headers": {
"type": "object"
},
"body": {
"type": "string"
}
},
"body": {
"type": "string"
}
},
"required": ["method", "url"],
"additionalProperties": false
}))
.plugin_id("example_extension".to_string())
.build();
"required": ["method", "url"],
"additionalProperties": false
}))
.plugin_id("example_extension".to_string())
.build();

assert_eq!(action, action_from_builder);
assert_eq!(action, action_from_builder);

let compiled = JSONSchema::compile(&action_from_builder.config_schema).expect("A valid schema");
let compiled =
JSONSchema::compile(&action_from_builder.config_schema).expect("A valid schema");

assert!(compiled.is_valid(&action_from_builder.config));
assert!(compiled.is_valid(&action_from_builder.config));
}
}
37 changes: 0 additions & 37 deletions plugin-core/crates/pdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,9 @@
// use extism::{CurrentPlugin, Error, Function, Json, UserData, Val, ValType};
// use serde_json::Value;

use extism_pdk::*;
use serde::{Deserialize, Serialize};
use serde_json::Value;
mod action;
pub use action::*;

//export a type used for register function
// #[derive(serde::Serialize, ToBytes)]
// #[encoding(Json)]
// pub struct Action {
// pub id: String,
// pub name: String,
// pub description: String,
// pub timestamp: String,
// }

// Define the Handle struct
// #[derive(Serialize, Deserialize, ToBytes)]
// #[encoding(Json)]
// pub struct Handle {
// pub id: String,
// pub position: String,
// pub r#type: String,
// }

// // Define the main Action struct
// #[derive(Serialize, Deserialize, ToBytes)]
// #[encoding(Json)]
// pub struct Action {
// pub trigger: bool,
// pub node_name: String,
// pub node_label: String,
// pub icon: String,
// pub description: String,
// pub handles: Vec<Handle>,
// pub variables: Vec<Value>, // Assuming variables is a list of arbitrary JSON values. Adjust if needed.
// pub config: Value, // Config as arbitrary JSON
// pub extension_id: String,
// }

#[derive(Deserialize, Serialize)]
pub struct Log {
pub time: String,
30 changes: 25 additions & 5 deletions plugin-core/crates/plugins/anything-http-plugin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -10,18 +10,38 @@ use serde_json::{json, Value};
pub fn register() -> FnResult<Action> {
let action: Action = Action::builder()
.trigger(false)
.node_name("http_action".to_string())
.node_label("HTTP Action".to_string())
.action_name("http_action".to_string())
.action_label("HTTP Action".to_string())
.icon("<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2.998 5.58a5.55 5.55 0 0 1 1.62-3.88l-.71-.7a6.45 6.45 0 0 0 0 9.16l.71-.7a5.55 5.55 0 0 1-1.62-3.88zm1.06 0a4.42 4.42 0 0 0 1.32 3.17l.71-.71a3.27 3.27 0 0 1-.76-1.12 3.45 3.45 0 0 1 0-2.67 3.22 3.22 0 0 1 .76-1.13l-.71-.71a4.46 4.46 0 0 0-1.32 3.17zm7.65 3.21l-.71-.71c.33-.32.59-.704.76-1.13a3.449 3.449 0 0 0 0-2.67 3.22 3.22 0 0 0-.76-1.13l.71-.7a4.468 4.468 0 0 1 0 6.34zM13.068 1l-.71.71a5.43 5.43 0 0 1 0 7.74l.71.71a6.45 6.45 0 0 0 0-9.16zM9.993 5.43a1.5 1.5 0 0 1-.245.98 2 2 0 0 1-.27.23l3.44 7.73-.92.4-.77-1.73h-5.54l-.77 1.73-.92-.4 3.44-7.73a1.52 1.52 0 0 1-.33-1.63 1.55 1.55 0 0 1 .56-.68 1.5 1.5 0 0 1 2.325 1.1zm-1.595-.34a.52.52 0 0 0-.25.14.52.52 0 0 0-.11.22.48.48 0 0 0 0 .29c.04.09.102.17.18.23a.54.54 0 0 0 .28.08.51.51 0 0 0 .5-.5.54.54 0 0 0-.08-.28.58.58 0 0 0-.23-.18.48.48 0 0 0-.29 0zm.23 2.05h-.27l-.87 1.94h2l-.86-1.94zm2.2 4.94l-.89-2h-2.88l-.89 2h4.66z\"/></svg>".to_string())
.description("Make an HTTP request".to_string())
.variables(vec![])
.config(serde_json::json!({
"method": "",
"url": "",
"method": "GET",
"url": "https://google.com",
"headers": {},
"body": ""
}))
.config_schema(serde_json::json!({
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
},
"url": {
"type": "string"
},
"headers": {
"type": "object"
},
"body": {
"type": "string"
}
},
"required": ["method", "url"],
"additionalProperties": false
}))
.extension_id("http".to_string())
.plugin_id("http".to_string())
.build();

Ok(action)

0 comments on commit 4d0abca

Please sign in to comment.