mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 16:02:18 +00:00
feat: add nodejs dispose function (#146)
* fix: add nodejs dispose function * fix: readme --------- Co-authored-by: Ivan Miletic <vnmiletic@gmail.com>
This commit is contained in:
co-authored by
Ivan Miletic
parent
64648aa4d5
commit
2a2d0d35d3
@@ -10,8 +10,8 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
napi = { version = "2.16", features = ["serde-json", "error_anyhow", "tokio_rt"] }
|
||||
napi-derive = "2.16"
|
||||
napi = { version = "2.16.2", features = ["serde-json", "error_anyhow", "tokio_rt"] }
|
||||
napi-derive = "2.16.2"
|
||||
serde_json = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
zen-engine = { path = "../../core/engine" }
|
||||
|
||||
@@ -40,6 +40,7 @@ import fs from 'fs/promises';
|
||||
|
||||
const decision = engine.createDecision(content);
|
||||
const result = await decision.evaluate({ input: 15 });
|
||||
engine.dispose();
|
||||
})();
|
||||
```
|
||||
|
||||
@@ -58,6 +59,7 @@ const loader = async (key: string) => fs.readFile(path.join(testDataRoot, key))(
|
||||
const engine = new ZenEngine({ loader });
|
||||
|
||||
const result = await engine.evaluate('jdm_graph1.json', { input: 5 });
|
||||
engine.dispose();
|
||||
})();
|
||||
```
|
||||
|
||||
|
||||
Vendored
+5
@@ -54,6 +54,11 @@ export class ZenEngine {
|
||||
evaluate(key: string, context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<ZenEngineResponse>
|
||||
createDecision(content: ZenDecisionContent | Buffer | object): ZenDecision
|
||||
getDecision(key: string): Promise<ZenDecision>
|
||||
/**
|
||||
* Function used to dispose memory allocated for loaders
|
||||
* In the future, it will likely be removed and made automatic
|
||||
*/
|
||||
dispose(): void
|
||||
}
|
||||
export class ZenEngineHandlerRequest {
|
||||
input: any
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
use napi::anyhow::anyhow;
|
||||
use napi::bindgen_prelude::Promise;
|
||||
use napi::threadsafe_function::{ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction};
|
||||
use napi::{Env, JsFunction};
|
||||
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
|
||||
|
||||
use zen_engine::handler::custom_node_adapter::{CustomNodeAdapter, CustomNodeRequest};
|
||||
use zen_engine::handler::node::{NodeResponse, NodeResult};
|
||||
|
||||
use crate::types::{ZenEngineHandlerRequest, ZenEngineHandlerResponse};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct CustomNode {
|
||||
function: Option<ThreadsafeFunction<ZenEngineHandlerRequest, ErrorStrategy::Fatal>>,
|
||||
}
|
||||
|
||||
impl Default for CustomNode {
|
||||
fn default() -> Self {
|
||||
Self { function: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomNode {
|
||||
pub fn try_new(env: &mut Env, function: JsFunction) -> napi::Result<Self> {
|
||||
let mut tsf = function.create_threadsafe_function(
|
||||
0,
|
||||
|cx: ThreadSafeCallContext<ZenEngineHandlerRequest>| Ok(vec![cx.value]),
|
||||
)?;
|
||||
|
||||
tsf.unref(env)?;
|
||||
|
||||
Ok(Self {
|
||||
pub fn new(tsf: ThreadsafeFunction<ZenEngineHandlerRequest, ErrorStrategy::Fatal>) -> Self {
|
||||
Self {
|
||||
function: Some(tsf),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ use crate::content::ZenDecisionContent;
|
||||
use crate::custom_node::CustomNode;
|
||||
use crate::decision::ZenDecision;
|
||||
use crate::loader::DecisionLoader;
|
||||
use crate::types::ZenEngineResponse;
|
||||
use crate::types::{ZenEngineHandlerRequest, ZenEngineResponse};
|
||||
use napi::anyhow::{anyhow, Context};
|
||||
use napi::bindgen_prelude::{Buffer, Either3};
|
||||
use napi::threadsafe_function::{ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction};
|
||||
use napi::{tokio, Env, JsFunction, JsObject};
|
||||
use napi_derive::napi;
|
||||
use serde_json::Value;
|
||||
@@ -15,6 +16,9 @@ use zen_engine::{DecisionEngine, EvaluationOptions};
|
||||
#[napi]
|
||||
pub struct ZenEngine {
|
||||
graph: Arc<DecisionEngine<DecisionLoader, CustomNode>>,
|
||||
|
||||
loader_ref: Option<ThreadsafeFunction<String, ErrorStrategy::Fatal>>,
|
||||
custom_handler_ref: Option<ThreadsafeFunction<ZenEngineHandlerRequest, ErrorStrategy::Fatal>>,
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
@@ -44,7 +48,7 @@ pub struct ZenEngineOptions {
|
||||
#[napi]
|
||||
impl ZenEngine {
|
||||
#[napi(constructor)]
|
||||
pub fn new(mut env: Env, options: Option<ZenEngineOptions>) -> napi::Result<Self> {
|
||||
pub fn new(options: Option<ZenEngineOptions>) -> napi::Result<Self> {
|
||||
let Some(opts) = options else {
|
||||
return Ok(Self {
|
||||
graph: DecisionEngine::new(
|
||||
@@ -52,21 +56,45 @@ impl ZenEngine {
|
||||
CustomNode::default().into(),
|
||||
)
|
||||
.into(),
|
||||
|
||||
loader_ref: None,
|
||||
custom_handler_ref: None,
|
||||
});
|
||||
};
|
||||
|
||||
let loader = match opts.loader {
|
||||
None => DecisionLoader::default(),
|
||||
Some(loader_fn) => DecisionLoader::try_new(&mut env, loader_fn)?,
|
||||
let loader_ref = match opts.loader {
|
||||
None => None,
|
||||
Some(l) => Some(l.create_threadsafe_function(
|
||||
0,
|
||||
|cx: ThreadSafeCallContext<String>| {
|
||||
cx.env.create_string(cx.value.as_str()).map(|v| vec![v])
|
||||
},
|
||||
)?),
|
||||
};
|
||||
|
||||
let custom_handler = match opts.custom_handler {
|
||||
let loader = match &loader_ref {
|
||||
None => DecisionLoader::default(),
|
||||
Some(loader_fn) => DecisionLoader::new(loader_fn.clone())?,
|
||||
};
|
||||
|
||||
let custom_handler_ref = match opts.custom_handler {
|
||||
None => None,
|
||||
Some(custom_handler_fn) => Some(custom_handler_fn.create_threadsafe_function(
|
||||
0,
|
||||
|cx: ThreadSafeCallContext<ZenEngineHandlerRequest>| Ok(vec![cx.value]),
|
||||
)?),
|
||||
};
|
||||
|
||||
let custom_handler = match &custom_handler_ref {
|
||||
None => CustomNode::default(),
|
||||
Some(custom_fn) => CustomNode::try_new(&mut env, custom_fn)?,
|
||||
Some(custom_fn) => CustomNode::new(custom_fn.clone()),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
graph: DecisionEngine::new(loader.into(), custom_handler.into()).into(),
|
||||
|
||||
loader_ref,
|
||||
custom_handler_ref,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -128,4 +156,17 @@ impl ZenEngine {
|
||||
|
||||
Ok(ZenDecision::from(decision))
|
||||
}
|
||||
|
||||
/// Function used to dispose memory allocated for loaders
|
||||
/// In the future, it will likely be removed and made automatic
|
||||
#[napi]
|
||||
pub fn dispose(&self) {
|
||||
if let Some(loader) = self.loader_ref.clone() {
|
||||
let _ = loader.abort();
|
||||
}
|
||||
|
||||
if let Some(loader) = self.custom_handler_ref.clone() {
|
||||
let _ = loader.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use napi::anyhow::anyhow;
|
||||
use napi::bindgen_prelude::{Buffer, Promise};
|
||||
use napi::threadsafe_function::{ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction};
|
||||
use napi::{Either, Env, JsFunction};
|
||||
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
|
||||
use napi::Either;
|
||||
|
||||
use zen_engine::loader::{DecisionLoader as DecisionLoaderTrait, LoaderError, LoaderResult};
|
||||
use zen_engine::model::DecisionContent;
|
||||
@@ -17,14 +17,7 @@ pub(crate) struct DecisionLoader {
|
||||
}
|
||||
|
||||
impl DecisionLoader {
|
||||
pub fn try_new(env: &mut Env, function: JsFunction) -> napi::Result<Self> {
|
||||
let mut tsf =
|
||||
function.create_threadsafe_function(0, |cx: ThreadSafeCallContext<String>| {
|
||||
cx.env.create_string(cx.value.as_str()).map(|v| vec![v])
|
||||
})?;
|
||||
|
||||
tsf.unref(env)?;
|
||||
|
||||
pub fn new(tsf: ThreadsafeFunction<String, ErrorStrategy::Fatal>) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
function: Some(tsf),
|
||||
})
|
||||
|
||||
@@ -29,6 +29,8 @@ describe('ZenEngine', () => {
|
||||
expect(r1.result.output).toEqual(10);
|
||||
expect(r2.result.output).toEqual(0);
|
||||
expect(r3.result.output).toEqual(10);
|
||||
|
||||
engine.dispose();
|
||||
}, 10000);
|
||||
|
||||
it('Evaluates decisions using getDecision', async () => {
|
||||
@@ -46,6 +48,8 @@ describe('ZenEngine', () => {
|
||||
expect(r1.result.output).toEqual(20);
|
||||
expect(r2.result.output).toEqual(0);
|
||||
expect(r3.result.output).toEqual(10);
|
||||
|
||||
engine.dispose();
|
||||
}, 10000);
|
||||
|
||||
it('Creates a decision from contents', async () => {
|
||||
@@ -80,6 +84,8 @@ describe('ZenEngine', () => {
|
||||
|
||||
const r = await engine.evaluate('custom.json', {a: 5});
|
||||
expect(r.result.data).toEqual(25);
|
||||
|
||||
engine.dispose();
|
||||
});
|
||||
|
||||
it('Parses ZenDecisionContent', async () => {
|
||||
|
||||
Reference in New Issue
Block a user