mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 00:02:18 +00:00
feat: improve nodejs bindings (#145)
This commit is contained in:
Vendored
+6
-2
@@ -8,7 +8,7 @@ export interface ZenEvaluateOptions {
|
||||
trace?: boolean
|
||||
}
|
||||
export interface ZenEngineOptions {
|
||||
loader?: (key: string) => Promise<Buffer>
|
||||
loader?: (key: string) => Promise<Buffer | ZenDecisionContent>
|
||||
customHandler?: (request: ZenEngineHandlerRequest) => Promise<ZenEngineHandlerResponse>
|
||||
}
|
||||
export function evaluateExpressionSync(expression: string, context?: any | undefined | null): any
|
||||
@@ -40,6 +40,10 @@ export interface DecisionNode {
|
||||
kind: string
|
||||
config: any
|
||||
}
|
||||
export class ZenDecisionContent {
|
||||
constructor(content: Buffer | object)
|
||||
toBuffer(): Buffer
|
||||
}
|
||||
export class ZenDecision {
|
||||
constructor()
|
||||
evaluate(context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<ZenEngineResponse>
|
||||
@@ -48,7 +52,7 @@ export class ZenDecision {
|
||||
export class ZenEngine {
|
||||
constructor(options?: ZenEngineOptions | undefined | null)
|
||||
evaluate(key: string, context: any, opts?: ZenEvaluateOptions | undefined | null): Promise<ZenEngineResponse>
|
||||
createDecision(content: Buffer): ZenDecision
|
||||
createDecision(content: ZenDecisionContent | Buffer | object): ZenDecision
|
||||
getDecision(key: string): Promise<ZenDecision>
|
||||
}
|
||||
export class ZenEngineHandlerRequest {
|
||||
|
||||
@@ -281,8 +281,9 @@ if (!nativeBinding) {
|
||||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { ZenDecision, ZenEngine, evaluateExpressionSync, evaluateUnaryExpressionSync, renderTemplateSync, evaluateExpression, evaluateUnaryExpression, renderTemplate, ZenEngineHandlerRequest } = nativeBinding
|
||||
const { ZenDecisionContent, ZenDecision, ZenEngine, evaluateExpressionSync, evaluateUnaryExpressionSync, renderTemplateSync, evaluateExpression, evaluateUnaryExpression, renderTemplate, ZenEngineHandlerRequest } = nativeBinding
|
||||
|
||||
module.exports.ZenDecisionContent = ZenDecisionContent
|
||||
module.exports.ZenDecision = ZenDecision
|
||||
module.exports.ZenEngine = ZenEngine
|
||||
module.exports.evaluateExpressionSync = evaluateExpressionSync
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use napi::bindgen_prelude::Buffer;
|
||||
use napi::{Either, Env, JsObject};
|
||||
use napi_derive::napi;
|
||||
use serde_json::Value;
|
||||
|
||||
use zen_engine::model::DecisionContent;
|
||||
|
||||
#[napi]
|
||||
pub struct ZenDecisionContent {
|
||||
pub(crate) inner: Arc<DecisionContent>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl ZenDecisionContent {
|
||||
#[napi(constructor)]
|
||||
pub fn new(env: Env, content: Either<Buffer, JsObject>) -> napi::Result<Self> {
|
||||
let decision_content: DecisionContent = match content {
|
||||
Either::A(buf) => serde_json::from_slice(buf.as_ref())?,
|
||||
Either::B(obj) => {
|
||||
let serde_val: Value = env.from_js_value(obj)?;
|
||||
serde_json::from_value(serde_val)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
inner: Arc::new(decision_content),
|
||||
})
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn to_buffer(&self) -> napi::Result<Buffer> {
|
||||
let content_vec = serde_json::to_vec(&self.inner.as_ref())?;
|
||||
Ok(Buffer::from(content_vec))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::content::ZenDecisionContent;
|
||||
use crate::custom_node::CustomNode;
|
||||
use crate::decision::ZenDecision;
|
||||
use crate::loader::DecisionLoader;
|
||||
use crate::types::ZenEngineResponse;
|
||||
use napi::anyhow::{anyhow, Context};
|
||||
use napi::bindgen_prelude::Buffer;
|
||||
use napi::{tokio, Env, JsFunction};
|
||||
use napi::bindgen_prelude::{Buffer, Either3};
|
||||
use napi::{tokio, Env, JsFunction, JsObject};
|
||||
use napi_derive::napi;
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
@@ -33,7 +34,7 @@ impl Default for ZenEvaluateOptions {
|
||||
|
||||
#[napi(object)]
|
||||
pub struct ZenEngineOptions {
|
||||
#[napi(ts_type = "(key: string) => Promise<Buffer>")]
|
||||
#[napi(ts_type = "(key: string) => Promise<Buffer | ZenDecisionContent>")]
|
||||
pub loader: Option<JsFunction>,
|
||||
|
||||
#[napi(ts_type = "(request: ZenEngineHandlerRequest) => Promise<ZenEngineHandlerResponse>")]
|
||||
@@ -99,9 +100,21 @@ impl ZenEngine {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn create_decision(&self, content: Buffer) -> napi::Result<ZenDecision> {
|
||||
let decision_content: DecisionContent = serde_json::from_slice(content.as_ref())?;
|
||||
let decision = self.graph.create_decision(Arc::new(decision_content));
|
||||
pub fn create_decision(
|
||||
&self,
|
||||
env: Env,
|
||||
content: Either3<&ZenDecisionContent, Buffer, JsObject>,
|
||||
) -> napi::Result<ZenDecision> {
|
||||
let decision_content: Arc<DecisionContent> = match content {
|
||||
Either3::A(c) => c.inner.clone(),
|
||||
Either3::B(buffer) => Arc::new(serde_json::from_slice(buffer.as_ref())?),
|
||||
Either3::C(obj) => {
|
||||
let serde_val: Value = env.from_js_value(obj)?;
|
||||
Arc::new(serde_json::from_value(serde_val)?)
|
||||
}
|
||||
};
|
||||
|
||||
let decision = self.graph.create_decision(decision_content);
|
||||
Ok(ZenDecision::from(decision))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod content;
|
||||
mod custom_node;
|
||||
mod decision;
|
||||
mod engine;
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
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::{Env, JsFunction};
|
||||
|
||||
use std::sync::Arc;
|
||||
use napi::{Either, Env, JsFunction};
|
||||
|
||||
use zen_engine::loader::{DecisionLoader as DecisionLoaderTrait, LoaderError, LoaderResult};
|
||||
use zen_engine::model::DecisionContent;
|
||||
|
||||
use crate::content::ZenDecisionContent;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct DecisionLoader {
|
||||
function: Option<ThreadsafeFunction<String, ErrorStrategy::Fatal>>,
|
||||
}
|
||||
|
||||
impl Default for DecisionLoader {
|
||||
fn default() -> Self {
|
||||
Self { function: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl DecisionLoader {
|
||||
pub fn try_new(env: &mut Env, function: JsFunction) -> napi::Result<Self> {
|
||||
let mut tsf =
|
||||
@@ -42,14 +39,14 @@ impl DecisionLoader {
|
||||
.into());
|
||||
};
|
||||
|
||||
let promise: Promise<Option<Buffer>> = function
|
||||
let promise: Promise<Option<Either<Buffer, &ZenDecisionContent>>> = function
|
||||
.clone()
|
||||
.call_async(key.to_string())
|
||||
.await
|
||||
.map_err(|e| LoaderError::Internal {
|
||||
key: key.to_string(),
|
||||
source: anyhow!(e.reason),
|
||||
})?;
|
||||
key: key.to_string(),
|
||||
source: anyhow!(e.reason),
|
||||
})?;
|
||||
|
||||
let result = promise.await.map_err(|e| LoaderError::Internal {
|
||||
key: key.to_string(),
|
||||
@@ -60,13 +57,17 @@ impl DecisionLoader {
|
||||
return Err(LoaderError::NotFound(key.to_string()).into());
|
||||
};
|
||||
|
||||
let decision_content =
|
||||
serde_json::from_slice(buffer.as_ref()).map_err(|e| LoaderError::Internal {
|
||||
key: key.to_string(),
|
||||
source: e.into(),
|
||||
})?;
|
||||
let decision_content = match buffer {
|
||||
Either::A(buf) => Arc::new(serde_json::from_slice(buf.as_ref()).map_err(|e| {
|
||||
LoaderError::Internal {
|
||||
key: key.to_string(),
|
||||
source: e.into(),
|
||||
}
|
||||
})?),
|
||||
Either::B(dc) => dc.inner.clone(),
|
||||
};
|
||||
|
||||
Ok(Arc::new(decision_content))
|
||||
Ok(decision_content)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
evaluateUnaryExpression,
|
||||
renderTemplate,
|
||||
evaluateExpressionSync,
|
||||
evaluateUnaryExpressionSync, renderTemplateSync
|
||||
evaluateUnaryExpressionSync, renderTemplateSync, ZenDecisionContent
|
||||
} from "../index";
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
@@ -81,6 +81,13 @@ describe('ZenEngine', () => {
|
||||
const r = await engine.evaluate('custom.json', {a: 5});
|
||||
expect(r.result.data).toEqual(25);
|
||||
});
|
||||
|
||||
it('Parses ZenDecisionContent', async () => {
|
||||
const decisionContent = new ZenDecisionContent(await loader('table.json'));
|
||||
|
||||
expect(decisionContent).toBeDefined();
|
||||
expect(decisionContent.toBuffer()).toBeDefined();
|
||||
});
|
||||
})
|
||||
|
||||
describe('Expressions', () => {
|
||||
|
||||
Reference in New Issue
Block a user