-
Notifications
You must be signed in to change notification settings - Fork 280
Re-design Dredd configuration handling #1311
Conversation
ec80419
to
a717352
Compare
I need to check Dredd's docs for references to the dropped CLI options. Will mark the respective checkbox in the PR when it's done. Update: Checked the docs, references to the deprecated options have not been found. Neither the reference to the NodeJS version. |
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 like the changes, only I did some more thinking about the deprecation warnings and I wonder whether it would make sense to literally break Dredd's behavior with this, i.e. to error and exit on the deprecated options. That seems actually more correct than to remove the warnings but to still accommodate the old (values of the) options.
That means, instead of warnings or removing warnings, we would turn them into errors and remove any coercing:
if (config.options.level) {
errors.push('REMOVED: ...');
}
if (config.options.l && !['silent', 'error', 'warning', 'warn', 'debug'].includes(config.options.l)) {
errors.push('REMOVED: ...');
}
We would also keep the existing errors, such as the one for hooksData
. What do you think about such approach? I'm sorry for proposing changes to the task after it's done, but it occurred to me only when I had a calm moment to stare to the diff here.
Makes sense, @honzajavorek. Then I will take this opportunity to adjust how validation and normalization of the config is performed currently. I would like to refrain from mutating the config object, and instead produce the next "resolved" config. |
Go for it 🚀 |
Heads up - I found bug in how the inlined API descriptions are being loaded in the config. This is a fix: be36252 The I have integration tests for API descriptions loading deep inside #1289 as well, but unfortunately they're dependent on other changes. |
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 it is on good path. The ramda code reads surprisingly well even for me who's not that skilled in it. Consider the behavior I pointed out in a comment. I don't think we need to implement all of it now, but we should get closer to it with the changes in this PR.
Updated the pull request with coercion of options. It looks somewhat complicated at certain places, I will go through simplifying it after all tests pass. There are a few other options not migrated (i.e. In a nutshell, I suggest to divide configuration options into three groups:
Removed options are omitted from the config object prior to any transformation, making the option design process more strict (you can't coerce removed option, maybe you mean deprecation). I will focus on finishing the coercion and rewriting the validation function next week. |
lib/configuration.js
Outdated
const coerceApiDescriptions = R.compose( | ||
mapIndexed((content, index) => ({ | ||
location: `configuration.apiDescriptions[${index}]`, | ||
content: R.when(R.has('content'), R.prop('content'), content), |
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 we had an issue in the previous implementation that we always mapped over the apiDescriptions
after coercing them to array. In case api descriptions already were an array, that would produce a nesting of their values.
Lines 191 to 196 in 582f252
// Transform apiDescriptions from strings to an array of objects | |
outConfig.apiDescriptions = coerceToArray(inConfig.apiDescriptions) | |
.map((apiDescription, i) => ({ | |
location: `configuration.apiDescriptions[${i}]`, | |
content: apiDescription, | |
})); |
I've introduced a check if individual entry of descriptions is already properly formatted then output it as-is.
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.
The intention is, externally, I'd like Dredd to have this as a plain array of strings:
["... content ...", "... content ..."]
Internally, I'd like it to be a more complex structure, an array of objects:
[
{ content: "...", location: "...", ... },
{ content: "...", location: "...", ... },
]
The location
is assigned property, user cannot specify it. When the API descriptions are provided inlined in the configuration of the Dredd class, it is assigned to configuration.apiDescriptions[original array index]
. If the API descriptions come from files or URLs, the location is set to foobar.apib
or http://example.com/foobar.apib
. I can imagine having apiDescriptions
supported in dredd.yml
as well, so perhaps we could have dredd.yml
like this:
apiDescriptions:
- "... content ..."
- "... content ..."
I think there's no reason to accept the more complex, internal structure on the input. If anything else than array of strings (or, for that matter, a single string) comes on the input, Dredd should error as misconfigured. I don't think that was the case currently, but if we're fixing this, I'd restrain from supporting the complex structure on input.
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 don't see my above comments addressed (we're accommodating the array if it's already with location, but I think we should reject it in such case; valid inputs are a single string or an array of strings).
Finished with rewriting the coercion logic. Tests pass 🎉 (related tests, I mean). |
46b5f5e
to
0561654
Compare
Notes
|
bfa0a68
to
08e8fbf
Compare
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.
Good job! I made a few comments. Some of them are nitpicks, some of them are to discuss. Some more remarks:
- I'm missing more unit tests for the configuration.js functions. E.g. a unit test for the function taking care of the
--user
to token header, etc. - I'm missing at least one integration test to verify the old way still works. We've replaced
options
everywhere, also in tests, which means nothing in the test suite actually tries to feed Dredd with nested options and we don't know whether it works as expected. - Is the PR description still valid? I think it should be updated as we pivoted from the original purpose of the PR.
- Users are given a warning that the options are now deprecated, but I see no information what should they do to prevent the warning and I see zero changes in the docs.
- I don't know what's the difference between
// TODO
and@todo
, but let's use just one. - I noticed you prefer
compose
overpipe
. I kind of grapple with the fact it reverses the order of reading the code, but I guess that's just a matter of preference and habits. - Would it make sense to
git pull --rebase origin master
the branch so we get rid of theMerge branch ...
commits?
lib/configuration.js
Outdated
const coerceApiDescriptions = R.compose( | ||
mapIndexed((content, index) => ({ | ||
location: `configuration.apiDescriptions[${index}]`, | ||
content: R.when(R.has('content'), R.prop('content'), content), |
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 don't see my above comments addressed (we're accommodating the array if it's already with location, but I think we should reject it in such case; valid inputs are a single string or an array of strings).
Thanks for review, @honzajavorek. I will address your comments and provide the changes.
|
Perhaps bikeshedding, but is
Also, there might already be some |
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 good, I found just nitpicks mostly and a few things perhaps worth addressing, but not really anything serious.
const defaultConfig = { | ||
http: {}, | ||
server: null, | ||
// emitter: new EventEmitter(), // TODO Merge + preserve prototype? |
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.
Nitpick: When I think about it, I'd appreciate if the comment was more verbose. In case we keep it here for a while, it might get murky when we forget the context. Don't be afraid to overcomment in this case:
const defaultConfig = {
http: {},
server: null,
// TODO Merge + preserve prototype?
//
// If the next line was uncommented, the subsequent merging machinery wouldn't be able to deal with
// the fact the EventEmitter isn't a plain object. We worked around it by making the code to treat the event emitter
// as a special case, but a more robust solution would be nicer.
//
// emitter: new EventEmitter(),
...
Just an example, perhaps it could be more brief 😄 I'm sometimes way too verbose with my writing.
In case of weird things, special cases, work arounds, etc., I'm all for writing tomes of wisdom for the next generations of readers into the code. Otherwise the suffering you went through to discover and solve the problem will be forgotten and someone else in the future will have to go through it again.
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, good call. I've provided detailed description below, where we explicitly set emitter
after merging with default config. I will mention it here as well.
const validateConfig = require('./validateConfig'); | ||
const { normalizeConfig } = require('./normalizeConfig'); | ||
|
||
const defaultConfig = { |
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 wonder if this shouldn't be called DEFAULT_CONFIG
so it's clearer in the code below this is a global static thing rather than a local variable
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.
No hard opinion on that :) We are not reusing it anywhere, so it's not global per say, it remains scoped to this module.
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.
We're exporting it and using it in the respective test file and I think the constant-like letter casing would convey better what the variable represents
}); | ||
}); | ||
|
||
it('with both "data" and "apiDescriptions"', () => { |
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.
👍 👏
09b4bdd
to
56c4600
Compare
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.
Done reviewing the latest changes. Please address my comments and add the short options handling. Then I think we're good to go.
}, | ||
}; | ||
applyConfiguration.resolveConfig = resolveConfig; | ||
applyConfiguration.DEFAULT_CONFIG = DEFAULT_CONFIG; |
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 see your point about collisions with builtin function properties. What we did in other files was underscore dangling so it never happens:
applyConfiguration._resolveConfig = resolveConfig;
applyConfiguration._DEFAULT_CONFIG = DEFAULT_CONFIG;
I wonder whether it's needed or not. Looking at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function I don't think we're likely to hit any of the prototype properties 🤔 Maybe we could remove the dangling from other files then (if you agree, we could do it in a separate PR and then we can remove https://github.com/apiaryio/dredd/blob/master/.eslintrc.js#L22-L25 as well).
0eea4cf
to
0006046
Compare
I see all my comments addressed. What's the state of the short options handling? Otherwise I think it's good to go 🚀 One nitpick, if you want to scope a commit, the parentheses are before the colon: - test: (resolveConfig) breaks down options test suits
+ test(resolveConfig): breaks down options test suits This is optional, in Dredd there is no convention around scoping the conventional changelog commits. Another nitpick, it would be nicer to rebase the branch and squash/drop stuff not to have the |
d183e5c
to
758add3
Compare
Comments addressed, handling of options aliases provided. I will rebase to get rid of |
758add3
to
db1127f
Compare
Marked |
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! 🚀 I found a few loose ends, but nothing which would block the PR:
- default for the server option is missing - Re-design Dredd configuration handling #1311 (comment)
- do we want to remove underscores dangling? (perhaps a separate issue and definitely a separate PR) Re-design Dredd configuration handling #1311 (comment)
-
@todo
vs// TODO
, we should use just one Re-design Dredd configuration handling #1311 (comment) - the
test: (resolveConfig) breaks down options test suits
commit format Re-design Dredd configuration handling #1311 (comment) - the issues created should link back to this PR for a reference (I did this right now)
Neither one of them is serious in any way and can be discussed and addressed later (or never). I'd focus on getting this merged now.
6a05df5
to
d97489f
Compare
d97489f
to
adf723a
Compare
🎉 This PR is included in version 10.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
🚀 Why this change?
Change log
config.options
intoconfig
l
andlevel
tologlevel
data
toapiDescriptions
user
to Authorizationheader
c
into booleancolor
c
,l
,q
) in deprecation schemasLoggers configuration adjusted(changes are too big as-is, logging to be refactored in a separate pull request)_coerceRemovedOptions
and respective testsoptions
to ensure it's supportedserver
key asendpoint
key in the root level of config. Setoptions.server
as-is📝 Related issues and Pull Requests
✅ What didn't I forget?
npm run lint