@onlineapps/cookbook-template-helpers 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +188 -0
- package/package.json +36 -0
- package/src/helpers/fileHelpers.js +201 -0
- package/src/index.js +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# @onlineapps/cookbook-template-helpers
|
|
2
|
+
|
|
3
|
+
Template helper functions for cookbook variable resolution. Helper functions enable data transformations directly in cookbook template variables.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @onlineapps/cookbook-template-helpers
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Helper functions can be used in cookbook template variables using the syntax:
|
|
14
|
+
```
|
|
15
|
+
{{helper_name(argument)}}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Arguments can be template variables themselves:
|
|
19
|
+
```
|
|
20
|
+
{{helper_name(steps.step_id.output.field)}}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Available Helpers
|
|
24
|
+
|
|
25
|
+
### string2file
|
|
26
|
+
|
|
27
|
+
Converts string content or inline descriptor to file descriptor. Uploads content to MinIO storage.
|
|
28
|
+
|
|
29
|
+
**Signature:**
|
|
30
|
+
```javascript
|
|
31
|
+
async function string2file(input, context)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Parameters:**
|
|
35
|
+
- `input` (string|Object) - String content or inline descriptor
|
|
36
|
+
- `context` (Object) - Workflow context (for storage access)
|
|
37
|
+
|
|
38
|
+
**Returns:**
|
|
39
|
+
- `Promise<Object>` - File descriptor
|
|
40
|
+
|
|
41
|
+
**Examples:**
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"input": {
|
|
46
|
+
"file": "{{string2file(steps.markdown.output.content)}}"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"input": {
|
|
54
|
+
"file": "{{string2file('Hello world')}}"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### file2string
|
|
60
|
+
|
|
61
|
+
Converts file descriptor to string. Downloads content from MinIO if needed.
|
|
62
|
+
|
|
63
|
+
**Signature:**
|
|
64
|
+
```javascript
|
|
65
|
+
async function file2string(descriptor, context)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Parameters:**
|
|
69
|
+
- `descriptor` (Object|string) - File descriptor or string
|
|
70
|
+
- `context` (Object) - Workflow context (for storage access)
|
|
71
|
+
|
|
72
|
+
**Returns:**
|
|
73
|
+
- `Promise<string>` - String content
|
|
74
|
+
|
|
75
|
+
**Examples:**
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"input": {
|
|
80
|
+
"markdown": "{{file2string(steps.pdf.output.file_descriptor)}}"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage in Cookbook
|
|
86
|
+
|
|
87
|
+
Helper functions are integrated into the cookbook executor. They are automatically available when resolving template variables.
|
|
88
|
+
|
|
89
|
+
**Example cookbook:**
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"version": "2.0.0",
|
|
94
|
+
"steps": [
|
|
95
|
+
{
|
|
96
|
+
"step_id": "get_markdown",
|
|
97
|
+
"type": "task",
|
|
98
|
+
"service": "hello-service",
|
|
99
|
+
"operation": "generate-markdown",
|
|
100
|
+
"input": { "name": "Karel" }
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"step_id": "create_pdf",
|
|
104
|
+
"type": "task",
|
|
105
|
+
"service": "pdfgen-service",
|
|
106
|
+
"operation": "md2pdf",
|
|
107
|
+
"depends_on": ["get_markdown"],
|
|
108
|
+
"input": {
|
|
109
|
+
"markdown": "{{file2string(steps.get_markdown.output.file_descriptor)}}"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Integration
|
|
117
|
+
|
|
118
|
+
Helper functions are passed to the cookbook executor:
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
const templateHelpers = require('@onlineapps/cookbook-template-helpers');
|
|
122
|
+
const { CookbookExecutor } = require('@onlineapps/cookbook-executor');
|
|
123
|
+
|
|
124
|
+
const executor = new CookbookExecutor(cookbook, {
|
|
125
|
+
helpers: templateHelpers
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Adding Custom Helpers
|
|
130
|
+
|
|
131
|
+
You can combine global helpers with custom helpers:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const templateHelpers = require('@onlineapps/cookbook-template-helpers');
|
|
135
|
+
const customHelpers = require('./customHelpers');
|
|
136
|
+
|
|
137
|
+
const allHelpers = {
|
|
138
|
+
...templateHelpers,
|
|
139
|
+
...customHelpers
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const executor = new CookbookExecutor(cookbook, {
|
|
143
|
+
helpers: allHelpers
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Helper Function Requirements
|
|
148
|
+
|
|
149
|
+
- **Async**: All helpers must be async functions (return Promise)
|
|
150
|
+
- **Context**: Helpers receive workflow context as last parameter
|
|
151
|
+
- **Idempotent**: Same input should produce same output
|
|
152
|
+
- **Error handling**: Should throw descriptive errors
|
|
153
|
+
|
|
154
|
+
## Dependencies
|
|
155
|
+
|
|
156
|
+
- `@onlineapps/conn-base-storage` - For MinIO storage operations
|
|
157
|
+
|
|
158
|
+
## Environment Variables
|
|
159
|
+
|
|
160
|
+
Helper functions use the following environment variables for storage access:
|
|
161
|
+
|
|
162
|
+
- `MINIO_HOST` - MinIO endpoint (default: `api_shared_storage`)
|
|
163
|
+
- `MINIO_PORT` - MinIO port (default: `9000`)
|
|
164
|
+
- `MINIO_ACCESS_KEY` - MinIO access key (default: `minioadmin`)
|
|
165
|
+
- `MINIO_SECRET_KEY` - MinIO secret key (default: `minioadmin`)
|
|
166
|
+
- `MINIO_USE_SSL` - Use SSL (default: `false`)
|
|
167
|
+
|
|
168
|
+
## File Descriptor Format
|
|
169
|
+
|
|
170
|
+
File descriptors have the following structure:
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
{
|
|
174
|
+
_descriptor: true,
|
|
175
|
+
type: 'file', // or 'inline'
|
|
176
|
+
storage_ref: 'minio://workflow/path/to/file', // for type='file'
|
|
177
|
+
content: '...', // for type='inline'
|
|
178
|
+
filename: 'document.txt',
|
|
179
|
+
content_type: 'text/plain',
|
|
180
|
+
size: 1234,
|
|
181
|
+
fingerprint: 'sha256...'
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
PROPRIETARY
|
|
188
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onlineapps/cookbook-template-helpers",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Template helper functions for cookbook variable resolution - string2file, file2string, and more",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test:watch": "jest --watch",
|
|
9
|
+
"test:coverage": "jest --coverage",
|
|
10
|
+
"docs": "jsdoc2md --files src/**/*.js > API.md"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cookbook",
|
|
14
|
+
"workflow",
|
|
15
|
+
"template",
|
|
16
|
+
"helpers",
|
|
17
|
+
"functions"
|
|
18
|
+
],
|
|
19
|
+
"author": "OnlineApps",
|
|
20
|
+
"license": "PROPRIETARY",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@onlineapps/conn-base-storage": "^1.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"jest": "^29.7.0"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"src"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* File helper functions for cookbook template resolution
|
|
5
|
+
* Converts between string content and file descriptors
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const StorageConnector = require('@onlineapps/conn-base-storage');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get storage instance from context or create new one
|
|
12
|
+
* @private
|
|
13
|
+
* @param {Object} context - Workflow context
|
|
14
|
+
* @returns {Promise<Object>} Storage connector instance
|
|
15
|
+
*/
|
|
16
|
+
async function getStorageFromContext(context) {
|
|
17
|
+
// If storage is already in context, use it
|
|
18
|
+
if (context._storage) {
|
|
19
|
+
return context._storage;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Otherwise create new storage instance
|
|
23
|
+
const storage = new StorageConnector({
|
|
24
|
+
endpoint: process.env.MINIO_HOST || 'api_shared_storage',
|
|
25
|
+
port: parseInt(process.env.MINIO_PORT || '9000', 10),
|
|
26
|
+
accessKey: process.env.MINIO_ACCESS_KEY || 'minioadmin',
|
|
27
|
+
secretKey: process.env.MINIO_SECRET_KEY || 'minioadmin',
|
|
28
|
+
useSSL: process.env.MINIO_USE_SSL === 'true'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Cache in context for reuse
|
|
32
|
+
context._storage = storage;
|
|
33
|
+
return storage;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convert string or inline descriptor to file descriptor
|
|
38
|
+
* Uploads content to MinIO and returns file descriptor
|
|
39
|
+
*
|
|
40
|
+
* @param {string|Object} input - String content or inline descriptor
|
|
41
|
+
* @param {Object} context - Workflow context (for storage access)
|
|
42
|
+
* @returns {Promise<Object>} File descriptor
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // String input
|
|
46
|
+
* const descriptor = await string2file('Hello world', context);
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Inline descriptor input
|
|
50
|
+
* const descriptor = await string2file({
|
|
51
|
+
* _descriptor: true,
|
|
52
|
+
* type: 'inline',
|
|
53
|
+
* content: 'Hello world',
|
|
54
|
+
* filename: 'greeting.txt'
|
|
55
|
+
* }, context);
|
|
56
|
+
*/
|
|
57
|
+
async function string2file(input, context) {
|
|
58
|
+
if (!input) {
|
|
59
|
+
throw new Error('[string2file] Input is required');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Pokud už je file descriptor, vrať ho
|
|
63
|
+
if (input._descriptor && input.type === 'file') {
|
|
64
|
+
return input;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Pokud je inline descriptor, převeď na file
|
|
68
|
+
if (input._descriptor && input.type === 'inline') {
|
|
69
|
+
const content = input.content;
|
|
70
|
+
if (typeof content !== 'string') {
|
|
71
|
+
throw new Error('[string2file] Inline descriptor content must be a string');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const storage = await getStorageFromContext(context);
|
|
75
|
+
const encoding = input.encoding || 'utf-8';
|
|
76
|
+
const buffer = Buffer.from(content, encoding);
|
|
77
|
+
|
|
78
|
+
// Upload do MinIO
|
|
79
|
+
const workflowId = context.workflow_id || context.workflowId || 'temp';
|
|
80
|
+
const stepId = context.step_id || context.stepId || 'helpers';
|
|
81
|
+
const pathPrefix = `helpers/${workflowId}/${stepId}`;
|
|
82
|
+
|
|
83
|
+
const result = await storage.uploadWithFingerprint(
|
|
84
|
+
'workflow',
|
|
85
|
+
buffer,
|
|
86
|
+
pathPrefix
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
_descriptor: true,
|
|
91
|
+
type: 'file',
|
|
92
|
+
storage_ref: `minio://workflow/${result.path}`,
|
|
93
|
+
filename: input.filename || 'content.txt',
|
|
94
|
+
content_type: input.content_type || 'text/plain',
|
|
95
|
+
size: buffer.length,
|
|
96
|
+
fingerprint: result.fingerprint
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Pokud je string, vytvoř file descriptor
|
|
101
|
+
if (typeof input === 'string') {
|
|
102
|
+
const storage = await getStorageFromContext(context);
|
|
103
|
+
const buffer = Buffer.from(input, 'utf-8');
|
|
104
|
+
|
|
105
|
+
const workflowId = context.workflow_id || context.workflowId || 'temp';
|
|
106
|
+
const stepId = context.step_id || context.stepId || 'helpers';
|
|
107
|
+
const pathPrefix = `helpers/${workflowId}/${stepId}`;
|
|
108
|
+
|
|
109
|
+
const result = await storage.uploadWithFingerprint(
|
|
110
|
+
'workflow',
|
|
111
|
+
buffer,
|
|
112
|
+
pathPrefix
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
_descriptor: true,
|
|
117
|
+
type: 'file',
|
|
118
|
+
storage_ref: `minio://workflow/${result.path}`,
|
|
119
|
+
filename: 'content.txt',
|
|
120
|
+
content_type: 'text/plain',
|
|
121
|
+
size: buffer.length,
|
|
122
|
+
fingerprint: result.fingerprint
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new Error('[string2file] Invalid input - expected string or descriptor');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Convert file descriptor to string
|
|
131
|
+
* Downloads content from MinIO if needed
|
|
132
|
+
*
|
|
133
|
+
* @param {Object|string} descriptor - File descriptor or string
|
|
134
|
+
* @param {Object} context - Workflow context (for storage access)
|
|
135
|
+
* @returns {Promise<string>} String content
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // File descriptor input
|
|
139
|
+
* const content = await file2string({
|
|
140
|
+
* _descriptor: true,
|
|
141
|
+
* type: 'file',
|
|
142
|
+
* storage_ref: 'minio://workflow/path/to/file',
|
|
143
|
+
* fingerprint: 'abc123...'
|
|
144
|
+
* }, context);
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* // Inline descriptor input
|
|
148
|
+
* const content = await file2string({
|
|
149
|
+
* _descriptor: true,
|
|
150
|
+
* type: 'inline',
|
|
151
|
+
* content: 'Hello world'
|
|
152
|
+
* }, context);
|
|
153
|
+
*/
|
|
154
|
+
async function file2string(descriptor, context) {
|
|
155
|
+
// Pokud není descriptor, vrať jak je (pokud je string)
|
|
156
|
+
if (!descriptor || !descriptor._descriptor) {
|
|
157
|
+
if (typeof descriptor === 'string') {
|
|
158
|
+
return descriptor;
|
|
159
|
+
}
|
|
160
|
+
return String(descriptor || '');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Pokud je inline, vrať content
|
|
164
|
+
if (descriptor.type === 'inline') {
|
|
165
|
+
return descriptor.content || '';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Pokud je file, stáhni z MinIO
|
|
169
|
+
if (descriptor.type === 'file') {
|
|
170
|
+
if (!descriptor.storage_ref) {
|
|
171
|
+
throw new Error('[file2string] File descriptor missing storage_ref');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const storage = await getStorageFromContext(context);
|
|
175
|
+
|
|
176
|
+
// Parse storage_ref: minio://workflow/path/to/file
|
|
177
|
+
const match = descriptor.storage_ref.match(/^minio:\/\/([^\/]+)\/(.+)$/);
|
|
178
|
+
if (!match) {
|
|
179
|
+
throw new Error(`[file2string] Invalid storage_ref format: ${descriptor.storage_ref}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const [, bucket, path] = match;
|
|
183
|
+
|
|
184
|
+
// Download with fingerprint verification
|
|
185
|
+
const content = await storage.downloadWithVerification(
|
|
186
|
+
bucket,
|
|
187
|
+
path,
|
|
188
|
+
descriptor.fingerprint
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return content.toString('utf-8');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
throw new Error(`[file2string] Invalid descriptor type: ${descriptor.type}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
string2file,
|
|
199
|
+
file2string
|
|
200
|
+
};
|
|
201
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module @onlineapps/cookbook-template-helpers
|
|
5
|
+
* @description Template helper functions for cookbook variable resolution
|
|
6
|
+
*
|
|
7
|
+
* Helper functions can be used in cookbook template variables:
|
|
8
|
+
* {{helper_name(arg)}}
|
|
9
|
+
*
|
|
10
|
+
* Example:
|
|
11
|
+
* {{string2file(steps.markdown.output.content)}}
|
|
12
|
+
* {{file2string(steps.pdf.output.file_descriptor)}}
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fileHelpers = require('./helpers/fileHelpers');
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
string2file: fileHelpers.string2file,
|
|
19
|
+
file2string: fileHelpers.file2string
|
|
20
|
+
};
|
|
21
|
+
|