-
Notifications
You must be signed in to change notification settings - Fork 78
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 URLs in lint --rules #41
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.
This looks great! Did you get a chance to add the test we were talking about? Using express to serve up a rules file so it can test the loading actually works?
speccy.js
Outdated
@@ -23,6 +23,7 @@ program | |||
.command('lint <file-or-url>') | |||
.option('-q, --quiet', 'reduce verbosity') | |||
.option('-r, --rules [ruleFile]', 'Provide multiple rules files', collect, []) | |||
// .option('-u, --url [ruleURL]', 'Provide multiple rules urls', collect, []) |
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.
👋
lib/loader.js
Outdated
@@ -86,7 +95,7 @@ function deepMergeRules(ruleNickname, skipRules, rules = []) { | |||
return rules; | |||
} | |||
|
|||
const loadRules = (loadFiles, skipRules = []) => { | |||
const loadRules = (loadFiles, skipRules = [], url = []) => { |
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.
Can probably get rid of this
package-lock.json
Outdated
@@ -9598,6 +9603,14 @@ | |||
} | |||
} | |||
}, | |||
"wget": { |
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.
Oops this is still in there
test/loader.test.js
Outdated
@@ -35,6 +35,11 @@ describe('loader.js', () => { | |||
] | |||
}; | |||
|
|||
it('accepts url rules', () => { | |||
const url = loader.loadRules(['https://raw.githubusercontent.com/wework/speccy/master/rules/default.json']); |
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.
My concern about this test is that it goes out to the actual internet. If github is down, the test suite will fail. If a contributor is offline, the test suite will fail.
To get around this most languages/frameworks have some sort of "web mock" library. In ruby it's literally called webmock, and in Node there's an awesome one called nock.
We could maybe do it with express, but nock might be a tad lighter, and avoid any awkward firewall related issues that could come from actually running a server locally.
lib/linter.js
Outdated
const lint = async (objectName, object, options = {}) => { | ||
|
||
console.log('now linting', object); | ||
console.log('activeRules', activeRules); |
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.
Problem 1: This is outputting empty in tests using this loadRuleFiles logic.
package.json
Outdated
@@ -6,7 +6,7 @@ | |||
"speccy": "./speccy.js" | |||
}, | |||
"scripts": { | |||
"test": "nyc --reporter=html --reporter=text mocha", |
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.
Don't let this change sneak in!
So, this change ended up involving a change @MikeRalphson pointed out, that loader knew too much about the shape of linter rules. A lot of stuff is getting async in this, which should make a few things better, but is making testing hell. All tests that use the helper
Is |
lib/linter.js
Outdated
|
||
const initialize = (options = {}) => { | ||
activeRules = {}; | ||
if (options.skip instanceof Array) { |
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.
Can use Array.isArray here (same as line 39)
c0d1e7c
to
5ba4ad5
Compare
lib/loader.js
Outdated
@@ -25,6 +31,20 @@ function readFileAsync(filename, encoding) { | |||
}); | |||
} | |||
|
|||
const fetchUrl = url => { | |||
return fetch(url).then(response => { |
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.
Since you are already using async/await
you can make this a little more less "thenny"
const fetchUrl = async (url) => {
const response = await fetch(url);
if (response.ok) {
try {
return await response.json();
} catch (error) {
return Promise.reject(new ReadError('Invalid JSON: ' + error.message));
}
}
if (response.status === 404) {
return Promise.reject(new OpenError('Page not found: ' + url));
}
return Promise.reject(new NetworkError('HTTP error: ' + response.status));
};
I am not entirely sure if returning Promise.reject
is the best approach or just throwing an 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.
Awesome thank you. This is better. I wrote it like this when I was failing to understand async/await, but think I got it now. :)
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.
Happy to help ^_^
Ended up getting involved with the code, cannot review now.
This PR is ready! Tested the command out manually and the tests cover everything else. |
lib/loader.js
Outdated
loadedRules = loadedRules.concat(deepMergeRules(files[f], skipRules)); | ||
} | ||
return loadedRules; | ||
const promises = files.map(file => recursivelyLoadRuleFiles(file, [], { verbose })); |
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.
Gah ok so loading URLs does not actually work it seems. It loads the file, but i dont think the promises ever get resolved. In my debugging I added these console lines:
console.log('Got some promises', promises);
const foo = await Promise.all(promises);
console.log('Did they resolve?', foo);
The output with --verbose is...
GET https://gist.githubusercontent.com/philsturgeon/b5295d402f2dc94b86aa32eddb1fa100/raw/1f1c3516294c8c8ebe294aea8b56c7116bce28c0/rules.json
Got some promises [ Promise { <pending> } ]
Specification is valid, with 0 lint errors
So it seems to junk out somewhere in the Promise.all... What the heck is that about?
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 death is happening mid fetchUrl, this code outputs got here https://gist.githubusercontent.com/philsturgeon/b5295d402f2dc94b86aa32eddb1fa100/raw/06eec564a2f8c9b972930981c165ebd7b00b8322/rules.json
but never outputs the data or an error...
const fetchUrl = async (url) => {
console.log('got here', url);
fetch(url).then(data => {
console.log('data', data)
}, err => {
console.log('err', err)
});
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.
You may be missing some wrapping try/catch mechanisms in your code.
lib/loader.js
Outdated
|
||
let data; | ||
if (file && file.startsWith('http')) { | ||
data = await fetchUrl(file); |
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.
Remember to wrap this in a try/catch
as it now can throw errors
25864fe
to
6be79f3
Compare
Done! Ready, actually got it working thanks to help from a million people. |
const lintObject = (objectName, object, options) => { | ||
const results = options.linter(objectName, object, options); | ||
|
||
// Update results |
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.
This method is mostly there to avoid needing to refactor validate.js to look for the new return value. In the future I'd like to continue this work, but I'm waiting for the dust to settle on openapi-kit before I worry about any of that. Let's accept this slight oddity and move on.
linter.lint('something', input, options); | ||
const ruleErrors = options.lintResults.map(result => result.rule.name); | ||
ruleErrors.should.deepEqual(expectedErrors); | ||
linter.initialize(); |
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.
New API for linter is easier to work with thanks to awesome advice from @MikeRalphson.
@maniator how is it looking? Need some thumbs! |
This makes the loader a bit less aware of internal linter workings, and theoretically speeds things up a little bit. Feedback from @maniator
Thank you @maniator @qwertypants @dangoosby for your help on this one! |
Happy to help @philsturgeon :-D |
@philsturgeon I don't often look @ github alerts :-( (slack is my friend...) |
const result = await asyncMap(files, file => recursivelyLoadRuleFiles(file, [], { verbose })); | ||
const flatten = [].concat(...result); | ||
// Unique copy of the array | ||
return [...(new Set(flatten))]; |
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.
Not sure the new Set
is needed here
/shrug
Speccy can now retrieve rules from provided url.