Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement cwl js expressions executor #6

Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
22367e5
Add new methods to CwlFile (values)
nsyzrantsev Jan 5, 2025
10cc2f6
Rename methods of CwlFile
nsyzrantsev Jan 5, 2025
036df69
Reformat CwlFile methods
nsyzrantsev Jan 5, 2025
6ec2d1e
Take into account the passed values in CwlFile methods
nsyzrantsev Jan 5, 2025
631f91d
Refactor code
nsyzrantsev Jan 5, 2025
49b1940
Define CLT and WF constants
nsyzrantsev Jan 5, 2025
bebe80a
Code refactoring
nsyzrantsev Jan 5, 2025
10c5640
Add js engine to cwl-parser
nsyzrantsev Jan 6, 2025
c9d5142
Reformat code
nsyzrantsev Jan 6, 2025
3f554e9
Support inputs, outputs in JsEngine
nsyzrantsev Jan 6, 2025
70cd75f
Fix bug with self object in JsEngine
nsyzrantsev Jan 6, 2025
da49de8
Reformat code
nsyzrantsev Jan 6, 2025
c99ebc9
Reformat code
nsyzrantsev Jan 6, 2025
4890ad4
Rename crate zefiro-cwl-parser to zefiro-core-cwl
nsyzrantsev Jan 6, 2025
719bc54
Rename JsEngine to JsExecutor and update tests in README
nsyzrantsev Jan 7, 2025
e7eb5e9
Refactor code
nsyzrantsev Jan 7, 2025
b67d9a3
Reformat code and change JsExecutor::new input type
nsyzrantsev Jan 7, 2025
b6dddf7
Reformat code
nsyzrantsev Jan 7, 2025
051830b
Reformat code
nsyzrantsev Jan 7, 2025
56d196d
Rename crate zefiro-core -> zefiro-pipeline
nsyzrantsev Jan 8, 2025
772b527
Reformat crates
nsyzrantsev Jan 8, 2025
8b66d0b
Update Cargo.toml
nsyzrantsev Jan 8, 2025
0221acb
Merge branch 'main' into 5-implement-a-logic-to-execute-and-render-js…
nsyzrantsev Jan 9, 2025
a77bf7c
Reformat code
nsyzrantsev Jan 9, 2025
916b4b8
Fix README
nsyzrantsev Jan 9, 2025
796fcdf
Fix ci and update cargo.toml
nsyzrantsev Jan 9, 2025
52ba976
Code reformatting
nsyzrantsev Jan 10, 2025
fc683c6
Fix crates names
nsyzrantsev Jan 10, 2025
1ffe517
Fix comments
nsyzrantsev Jan 10, 2025
bd03917
Fix formatting fails
nsyzrantsev Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ edition = "2021"
[workspace]
members = [
"zefiro-cli",
"zefiro-core",
"zefiro-core/zefiro-cwl-parser",
"zefiro-pipeline-engine",
"zefiro-pipeline-engine/zefiro-cwl-parser",
"zefiro-pipeline-engine/zefiro-cwl-js-exec",
"zefiro-ui"
]
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**Code**: [![codecov](https://codecov.io/gh/zefiroproj/zefiro/graph/badge.svg?token=5DgmM1KzuQ)](https://codecov.io/gh/zefiroproj/zefiro)

# Zefiro - fast, scalable and simple engine to manage workflows
# Zefiro - fast, scalable and simple engine to manage workflows

⚠️ This project is a Work In Progress, and is very far from being suitable for use ⚠️
6 changes: 3 additions & 3 deletions release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ export GIT_CLIFF_TEMPLATE="\
- {% if commit.breaking %}(breaking) {% endif %}{{ commit.message | upper_first }} ({{ commit.id | truncate(length=7, end=\"\") }})\
{% endfor %}
{% endfor %}"
if [ ! -f "examples/detailed.toml" ]; then
echo "Error: examples/detailed.toml not found"
if [ ! -f "test_data/detailed.toml" ]; then
echo "Error: test_data/detailed.toml not found"
exit 1
fi
changelog=$(git cliff --config examples/detailed.toml --unreleased --strip all)
changelog=$(git cliff --config test_data/detailed.toml --unreleased --strip all)
nsyzrantsev marked this conversation as resolved.
Show resolved Hide resolved

# create a signed tag
git -c user.name="zefiro" \
Expand Down
77 changes: 0 additions & 77 deletions zefiro-core/zefiro-cwl-parser/README.md

This file was deleted.

This file was deleted.

46 changes: 0 additions & 46 deletions zefiro-core/zefiro-cwl-parser/src/values/types.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "zefiro-core"
name = "zefiro-pipeline-engine"
version = "0.1.0"
edition = "2021"
rust-version = "1.83.0"
File renamed without changes.
25 changes: 25 additions & 0 deletions zefiro-pipeline-engine/zefiro-cwl-js-exec/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "zefiro-cwl-js-exec"
version = "0.1.0"
description = """
"The Common Workflow Language (CWL) object model that used in `zefiro`.
"""
nsyzrantsev marked this conversation as resolved.
Show resolved Hide resolved
authors = [
"Nikita Syzrantsev <[email protected]>"
]
categories = ["JavaScript", "CWL"]
keywords = ["JavaScript", "CWL Expressions"]
edition = "2021"
homepage = "https://github.com/zefiroproj/zefiro/zefiro-core/zefiro-cwl-js-exec"
license = "Apache-2.0"
readme = "README.md"
nsyzrantsev marked this conversation as resolved.
Show resolved Hide resolved
repository = "https://github.com/zefiroproj/zefiro"
rust-version = "1.83.0"

[dependencies]
anyhow = "1.0.95"
deno_core = "0.329.0"
serde_json = "1.0.135"

[dev-dependencies]
rstest = "0.24.0"
79 changes: 79 additions & 0 deletions zefiro-pipeline-engine/zefiro-cwl-js-exec/src/exec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use anyhow::{Context, Error};
use deno_core::{serde_json, serde_v8, v8, JsRuntime};
use serde_json::Value;

pub struct JsExecutor {
runtime: JsRuntime,
}

impl JsExecutor {
/// Creates a new `JsExecutor` with given `cwl_inputs` and `cwl_self`.
pub fn new(cwl_inputs: &Value, cwl_self: &Value) -> Result<Self, Error> {
let mut runtime = JsRuntime::new(Default::default());
let init_script = format!(
r#"const inputs = {}; const self = {};"#,
cwl_inputs, cwl_self
);

runtime
.execute_script("<init>", init_script)
.context("Failed to initialize JavaScript context")?;

Ok(Self { runtime })
}
nsyzrantsev marked this conversation as resolved.
Show resolved Hide resolved

/// Executes JavaScript `script` and returns the result as a string.
pub fn run(&mut self, script: &str) -> Result<String, Error> {
let result = self
.runtime
.execute_script("<eval>", script.to_string())
.context("Failed to execute JavaScript expression")?;

let scope = &mut self.runtime.handle_scope();
let local_result = v8::Local::new(scope, result);
let result_json: serde_json::Value =
serde_v8::from_v8(scope, local_result).context("Failed to deserialize result")?;

Ok(result_json.to_string())
}
}

#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
use serde_json::json;

#[rstest]
#[case(
json!({
"in_fastq": {
"class": "File",
"location": "/path/to/input.fastq",
"size": 1024 * 1024 * 512
}
}),
json!([{ "location": "/path/to/output.fastq" }]),
"inputs.in_fastq.size / (1024 * 1024) * 2;",
"1024"
)]
#[case(
json!({ "output_location_subdir": "output/" }),
json!([{ "location": "/path/to/output.fastq", "nameroot": "output" }]),
"self[0].location = inputs.output_location_subdir + self[0].nameroot + '.fq'; self[0];",
json!({ "location": "output/output.fq", "nameroot": "output" }).to_string()
)]
fn test_jsexecutor_run(
#[case] cwl_inputs: Value,
#[case] cwl_self: Value,
#[case] js_script: &str,
#[case] expected_result: String,
) {
let mut executor = JsExecutor::new(&cwl_inputs, &cwl_self)
.expect("Failed to initialize JavaScript engine");
let result = executor
.run(js_script)
.expect("JavaScript execution failed");
assert_eq!(result, expected_result);
}
}
3 changes: 3 additions & 0 deletions zefiro-pipeline-engine/zefiro-cwl-js-exec/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod exec;

pub use crate::exec::JsExecutor;
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ description = """
authors = [
"Nikita Syzrantsev <[email protected]>"
]
categories = ["CWL serialization", "CWL deserialization", "Common Workflow Language"]
categories = ["Serialization", "Deserialization", "Parsing"]
keywords = ["CWL", "CommandLineTool", "Workflow"]
edition = "2021"
homepage = "https://github.com/zefiroproj/zefiro"
homepage = "https://github.com/zefiroproj/zefiro/zefiro-core/zefiro-cwl-parser"
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/zefiroproj/zefiro"
Expand All @@ -21,6 +21,7 @@ anyhow = "1.0.95"
serde = { version = "1.0.216", features = ["derive"] }
serde_with = "3.12.0"
serde_yaml = "0.9.34"
sha1 = "0.10.6"

[dev-dependencies]
rstest = "0.24.0"
Expand Down
88 changes: 88 additions & 0 deletions zefiro-pipeline-engine/zefiro-cwl-parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# zefiro-cwl-parser

A Rust library for parsing and working with Common Workflow Language (CWL) documents.

## Overview

* Supports **only some fields** of CWL **v1.2** specification (see description of structures in the code)
* Can serialize and deserialize [CommandLineTool](https://www.commonwl.org/v1.2/CommandLineTool.html) and [Workflow](https://www.commonwl.org/v1.2/Workflow.html) documents

## Usage

Add this to your `Cargo.toml`:

```toml
[dependencies]
zefiro-cwl-parser = "0.1.0"
```


### How to parse CWL Schema document?

```rust
use zefiro_cwl_parser::CwlSchema;

fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse from file
let schema = CwlSchema::from_path("test_data/cwl/clt-step-schema.yml").unwrap();

// Parse from string
let yaml_str = r#"
cwlVersion: v1.2
class: CommandLineTool
id: step
inputs:
- id: in_file
type: File
inputBinding:
prefix: --in-file
- id: out_file
type: string
default: "output.txt"
inputBinding:
prefix: --out-file
- id: output_location_subdir
type: string
default: output/
outputs:
- id: out_file
type: File
outputBinding:
glob: $(inputs.out_file)
outputEval: ${self[0].location += inputs.output_location_subdir; return self[0]}
nsyzrantsev marked this conversation as resolved.
Show resolved Hide resolved
requirements:
- class: DockerRequirement
dockerPull: step-image-uri:1.0
- class: InlineJavascriptRequirement
"#;
let schema = CwlSchema::from_string(yaml_str).unwrap();

Ok(())
}
```


### How to parse CWL Values document?

```rust
use zefiro_cwl_parser::CwlValues;


fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse input values from file
let values = CwlValues::from_path("test_data/cwl/clt-step-values.yml").unwrap();

// Create values from string
let yaml_input = r#"
input_file:
class: File
location: 's3://bucket/input.txt'
output_file: 'output.txt'
"#;
let values = CwlValues::from_string(yaml_input).unwrap();

Ok(())
}
```

### How to execute JavaScript expressions?
nsyzrantsev marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[doc = include_str!("../README.md")]
pub mod schema;
pub mod values;

Expand Down
Loading
Loading