-
Notifications
You must be signed in to change notification settings - Fork 13
feat: support for re-usable global functions in workflows #894
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: support for re-usable global functions in workflows #894
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work as always @doc-han
I'd like to run more tests tomorrow, but here's a few quick thoughts!
I would like to see a lot more testing around the globals. Probably in the runtime package but maybe in integration-tests/execute too. I've been doing local testing like this:
And I'd like to see that sort of thing reflected in unit tests somewhere. |
@doc-han this is fantastic. What we need now is:
And some important notes: OperationsMy biggest worry is this: A user declares a cool hlper function
They call it from their job code:
And then... EXCEPTION!!
At least the new error reporting stuff should give a clue. But why did this fail? What's this The problem of course is that This gets us into explaining Operations and a whole level of details I don't want to engage users with (this is a huge tension in the docs right now). We can't lint for this stuff. It's perfectly legit to export a non operation. It's also cool to declare your own operation. It's just the usage in job code that's wong. Solutions?
But I mean, that's not terribly clean for users. And because it's dynamic it might be a bit harder to make this even work? We'd have to dynamically inject the globals right? Can we detect this particular case and raise a better error or warning? I don't really know where we'd do that... ValidationThere's quite a lot that can go wrong with globals, including:
I think all these can be validated at compile time. So we should run the compiler and throw if we detect any major problems. Also, maybe we should have a rule like: globals are immutable, so you're not allowed to export any We should probably create a new issue for this and deal with it later. Can I implement a whole adaptor in globals?Kinda yeah. Is that a problem? It feels like abuse of the feature, but I also don't see a problem. It might help people out. But! The big thing here is that, unlike unlike adpators, you cannot import anything into your globals. You can't use npm stuff. |
|
||
// FIXME: when expression isn't a string, additional stuff isn't loaded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to look at this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. expression in this scope is typed string | Operation[]
.
It seems Operation[]
type is only reached via tests. CLI always has expression being a string
.
If this assumption is accurate and expression in runtime is approx. always a string
, then there's no problem.
Here's the condition that loads-module only when it's a string.
kit/packages/runtime/src/execute/expression.ts
Lines 233 to 251 in 54dd347
if (typeof expression === 'string') { | |
const exports = await loadModule(expression, { | |
...opts.linker, | |
// allow module paths and versions to be overriden from the defaults | |
modules: mergeLinkerOptions(opts.linker?.modules, moduleOverrides), | |
context, | |
log: opts.logger, | |
}); | |
const operations = exports.default; | |
return { | |
operations, | |
...exports, | |
} as JobModule; | |
} else { | |
if (opts.forceSandbox) { | |
throw new InputError('Invalid arguments: jobs must be strings'); | |
} | |
return { operations: expression as Operation[] }; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Yes, usually the expression comes in as a string, but for some unit tests we do pass functions straight in.
it's a fair constraint that if you're passing a function directly, we don't do any environment preparation. We don't load globals, we don't load a sandbox.
I don't want to leave this comment here. It's not meaningul.
- We could move it to the
prepareJob
declaration. It's not a fixme, but it's correct docs that if you pass a live function, it'll bypass the sandbox, globals etc. - We could put out a warning in the logger. Given that it only affects tests, I don't think this is neccessary
Let's move it to prepareJob
and remove the FIXME
I believe Fetch should also be banned. so that all fetch calls go through our adaptors. The globals to me sound like helpful utils for doing internal work. anything external has to go through adaptors. |
I am happy to make that a constraint, at least for now. I'll update the docs PR to make this clear |
|
||
// FIXME: when expression isn't a string, additional stuff isn't loaded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Yes, usually the expression comes in as a string, but for some unit tests we do pass functions straight in.
it's a fair constraint that if you're passing a function directly, we don't do any environment preparation. We don't load globals, we don't load a sandbox.
I don't want to leave this comment here. It's not meaningul.
- We could move it to the
prepareJob
declaration. It's not a fixme, but it's correct docs that if you pass a live function, it'll bypass the sandbox, globals etc. - We could put out a warning in the logger. Given that it only affects tests, I don't think this is neccessary
Let's move it to prepareJob
and remove the FIXME
|
TODO: the compiler needs to know what global functions are available so that it can not try to associate them with with the adaptor |
Way to go: |
See #925 |
Just a thought: because we've changed the workflow.json structure, CLI deploy might start breaking. Need to test this. |
Another thought from Stu: how do we expose the globals to the type system for code assist? |
This is looking tricky at the moment.
Compilation happens way before runtime things happen. OR we do static analysis to extract the exported identifiers and then pass to the compilation step for jobs. Hence, global functions have to always be compiled first before any job is compiled. cc: @josephjclark |
Or we export We'll now have |
Possible collision between user defined global function identifiers & exported adaptor methods. |
@doc-han let's talk about this on Monday! |
From our meeting:
|
Yes to |
eg. openfn --globals ./path-to-globals.js
cli argument |
TODO: review docs OpenFn/docs#644 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great - just one question about a test. I'm likely to release this as-is though - I've run some basic tests and it looks fine
@@ -244,3 +244,37 @@ export default [each( | |||
const { code: result } = compile(source); | |||
t.is(result, expected); | |||
}); | |||
|
|||
// TODO: this is error prone and shouldn't be desired |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this error prone? Do we need to take a look at this test?
t.deepEqual(res, ['lightning', 'compiler']); | ||
}); | ||
|
||
// TODO: do we want to pick up default exports? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, let's ignore export default for now
Short Description
Introduces support for re-usable global functions in workflows
Fixes #648
Implementation Details
Runtime
workflow.globals
which contains some inline code with exported functions.CLI
QA Notes
workflow.globals
.AI Usage
Please disclose how you've used AI in this work (it's cool, we just want to know!):
You can read more details in our Responsible AI Policy
Release branch checklist
Delete this section if this is not a release PR.
If this IS a release branch:
pnpm changeset version
from root to bump versionspnpm install
pnpm changeset tag
to generate tagsgit push --tags
Tags may need updating if commits come in after the tags are first generated.