-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
Support non-function exports when including variables from js files #7540
Conversation
I held off on adding any documentation until I get some feedback on whether this approach is in the right direction. Happy to contribute some once it's been given a look. |
@srapp thanks for this PR. In general it looks fine to me, although still use case is not perfectly clear. @erikerikson what do you think about that proposal? Do you see any implications it may introduce? |
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.
I haven't looked deeply and don't recall the history of the choice to only allow a function to be returned. I think it would probably be helpful to look up the changes to the code over time and the associated discussions.
I suspect that, as hinted by my comments on the code, the implementer was looking at the diversity of possible returns and chose to keep it simple. Perhaps they even started by accepting the return of objects but realized they could get concrete values but also things that couldn't easily be dealt with or identified. Finally, I suspect that as they started to build out the code to deal with that, they simplified to avoid an unexpected expansion of scope and work. Maybe they just wanted to return a function.
Personally, it seems like a fine thing to increase the richness of what the JavaScript can return. I believe that historically there has been a constraint that the module code must be synchronous. Maybe my thoughts about handling specific return type constraints is pedantic. If so, apologies, I'm just waking up, on my phone, and avoiding going deep.
} else { | ||
returnValueFunction = jsFile; | ||
returnValue = jsFile; |
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.
I covered this in my comment on 1914 but you may not have an object or even a concrete value. What if jsFile === Error('')
?
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.
Totally possible, technically an Error
would be an object, though perhaps not one that we'd want to insert the values of. Same issue could happen though if a function returned an Error
too. So what should be the handling of that?
I think we could probably continue building a list of things we don't want to process that are technically objects, and I think we can't prevent the users from returning whatever they want. I'm not sure whether we want to try to keep up with a list of things we don't want to explicitly support, or rather assume the user is only going to be returning useful values for configuration. If an error has occurred - I think the advice would be to throw that error, rather than return it.
As for when an error is thrown, I think currently we just let that bubble up normally, though it's not wrapped in a Serverless error class at the moment. We could do some try/catching here to wrap it if that makes sense.
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.
I think you're calling me out for being a bit too pedantic and I agree. Reasonableness and dev awareness was assumed before and I shouldn't ask for elevation above that previous bar. The previous paradigm was to check for the one type of valid return (i.e. a function) but not to vet the result of the function's execution. Perhaps some positive case checking makes sense but since a non-function return is the equivalent of the function return value, I feel like I have little standing to request more. Mariusz may have a stronger opinion.
For the record and what it's worth, I don't have merge rights and can't give the green light so I'm just attempting to be helpful. Despite that, sorry for the slow responses.
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.
@erikerikson great thanks for that input. Indeed there can be anything exported by a module (even undefined
).
But as long as we do not validate the value that is returned by eventual function resolver, for consistency we also should not validate a directly provided value.
I think real concern comes from a design proposal: where we treat a given value as resolved as long as it's not a function and otherwise we treat a function as value resolver.
I have mixed feelings on that, it definitely can turn to be confusing, e.g. in current form we accept also a function as final resolved value (it can be returned by function resolver)
It'll work better if we restrict and validate the final valueToPopulateResolved
. Ideally we should just support JSON values, and simplified validation of that would be to reject all values for which typeof
returns 'undefined
, 'symbol'
or 'function'
.
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.
@erikerikson, @medikoo thank you both for the feedback! Sorry it's taken a bit to respond, I'll try to stay on top of this PR a bit better 😅
@medikoo I think we're in agreement here - that currently we don't really check the type of valueToPopulateResolved
, regardless of how that value is provided (function invocation or otherwise). Focusing on valueToPopulateResolved
and evaluating whether we've landed on a valid result type makes a lot of sense to me.
If I'm not mistaken, it looks like we already handle this for undefined
values: https://github.com/serverless/serverless/blob/master/lib/classes/Variables.js#L728. I've updated this PR to also throw an error if the resolved value is a symbol
or function
.
@srapp is this ready to take? If so please re-request review. Thank you! |
@medikoo Yes it is, barring documentation. I'll see if I can draft the docs this afternoon |
efc99a3
to
dae73d6
Compare
@medikoo I've made a minor change to the docs, just modifying the current example to export a constant value in one case, rather than a function. |
Codecov Report
@@ Coverage Diff @@
## master #7540 +/- ##
==========================================
+ Coverage 87.99% 88.00% +0.01%
==========================================
Files 241 245 +4
Lines 9120 9253 +133
==========================================
+ Hits 8025 8143 +118
- Misses 1095 1110 +15
Continue to review full report at Codecov.
|
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.
@srapp looks great! I've proposed just one minor style improvement, and we should be good to go
lib/classes/Variables.js
Outdated
|
||
return BbPromise.resolve(valueToPopulate).then(valueToPopulateResolved => { | ||
let deepProperties = variableString.replace(matchedFileRefString, ''); | ||
deepProperties = deepProperties.slice(1).split('.'); | ||
deepProperties.splice(0, 1); | ||
return this.getDeeperValue(deepProperties, valueToPopulateResolved).then( | ||
deepValueToPopulateResolved => { | ||
if (typeof deepValueToPopulateResolved === 'undefined') { | ||
if (['undefined', 'symbol', 'function'].includes(typeof deepValueToPopulateResolved)) { |
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.
Let's seclude that to maybe nonJsonTypes
set in module body, and validate via:
if (nonJsonTypes.has(typeof deepValueToPopulateResolved)) {
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.
Thank you @srapp !
What did you implement
Currently, when you import a
.js
file in your configuration, serverless only supports functions. This change also allows referencing non-function exports from a.js
file.Closes #7443
How can we verify it
config.js
serverless.yml
Command:
Expected output:
Todos
Useful Scripts
npm run test:ci
--> Run all validation checks on proposed changesnpm run lint:updated
--> Lint all the updated filesnpm run lint:fix
--> Automatically fix lint problems (if possible)npm run prettier-check:updated
--> Check if updated files adhere to Prettier confignpm run prettify:updated
--> Prettify all the updated filesIs this ready for review?: YES
Is it a breaking change?: NO