-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
[Swagger] Specs are valid, when only warnings are shown #4294
[Swagger] Specs are valid, when only warnings are shown #4294
Conversation
Unfortunatley I don't know why some tests are now failing, if someone can point me in the right direction, I'll gladly fix it. (with npm test locally all went well) Edit: Now they have passed ? |
Sorry I forgot to mention it earlier, but the original problem was the title of the PR (which started with |
If I'm following correctly, the objective was to make the resulting badge consistent with the Swagger validator by handling warning-only level "'errors". As noted in the PR description, using the sample https://petstore3.swagger.io/api/v3/openapi.json returns valid with the Swagger validator, but invalid for Shields http://validator.swagger.io/validator?url=https://petstore3.swagger.io/api/v3/openapi.json Prod Shields However, AFACIT the staging app for this PR is still saying the sample is invalid Did I get any of that wrong? If not, does that mean the original problem still exists? |
You are right, I tested with my own specification, located here, where it works. Ill look into it and update the PR |
It most definitly is.
|
I'm not entierly sure if this is the correct approach, as it will be a breaking change. Is this how you intended it or how should I do it? Locally now both the json and yaml spec show a valid spec, the tests Im currently fixing |
services/swagger/swagger.service.js
Outdated
@@ -22,20 +22,21 @@ module.exports = class SwaggerValidatorService extends BaseJsonService { | |||
static get route() { | |||
return { | |||
base: 'swagger/valid/2.0', | |||
pattern: ':scheme(http|https)?/:url*', | |||
pattern: ':scheme(http|https)?/:fileExtension(json|yaml)?/: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.
We should move the spec url path from route parameters to a single query param, with a redirector to maintain backwards compatibility for existing badges.
The Website service is probably a decent reference, https://github.com/badges/shields/pull/4028/files
shields/services/website/website.service.js
Lines 41 to 47 in 7f54e09
static get route() { | |
return { | |
base: '', | |
pattern: 'website', | |
queryParamSchema: queryParamSchema.concat(urlQueryParamSchema), | |
} | |
} |
https://github.com/badges/shields/blob/master/services/website/website-redirect.service.js
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.
Actually the TwitterUrl badge may be a simpler/better reference, as neither it nor this Swagger service had the same complexity the Website badge did
https://github.com/badges/shields/blob/master/services/twitter/twitter.service.js
https://github.com/badges/shields/blob/master/services/twitter/twitter-redirect.service.js
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 managed to move it to a query param, will look into the redirector in the following week
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.
So I wrote the redirector, and theoretically its working, but I've got one problem
This
http://localhost:8080/swagger/valid/2.0/https/raw.githubusercontent.com%2FOAI%2FOpenAPI-Specification%2Fmaster%2Fexamples%2Fv2.0%2Fjson%2Fp.etstore-expanded.json
will get
http://localhost:8080/swagger/valid/3.0/spec.json?url=https%3A%2F%2Fraw.githubusercontent.com%2FOAI%2FOpenAPI-Specification%2Fmaster%2Fexamples%2Fv2.0%2Fjson%2Fp.etstore-expanded
So the file extension does get appended on my path instead of begin part of the pattern. Any idea why?
Here the code:
module.exports = [
redirector({
category: 'other',
name: 'SwaggerUrlRedirect',
route: {
base: 'swagger/valid/2.0',
pattern: ':scheme(http|https)?/:url*',
},
transformPath: () => `/swagger/valid/3.0/spec`,
transformQueryParams: ({ scheme, url }) => ({
url: `${scheme}://${url}`
}),
dateAdded: new Date('2019-11-03'),
}),
]
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.
For the route pattern
, try this instead:
pattern: ':scheme(http|https)?/:url+',
Also, I don't think the scheme
portion should really be optional (I know it was already, just think we should also change that too given what we are issuing to the upstream API)
shields/services/swagger/swagger.service.js
Lines 52 to 63 in 7f54e09
async fetch({ scheme, urlF }) { | |
const url = 'http://validator.swagger.io/validator/debug' | |
return this._requestJson({ | |
url, | |
schema: validatorSchema, | |
options: { | |
qs: { | |
url: `${scheme}://${urlF}`, | |
}, | |
}, | |
}) | |
} |
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.
Did you also remove the ?
from the scheme?
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.
Tried with removing and without yes
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.
Ah okay, would you mind trying :url(.+)
as well? Sorry I know I've seen this before recently just don't remember off hand what the fix was
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 change also tried with * and (:url.+) and :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.
Could this be some sort of issue with the badge formats? Supported is svg and json ... and the .json is interpreted as the format of the badge when I see correctly
…orted swagger, WIP redirector
I did implement the not found feature, and pushed the (currently not working) redirect, maybe you can see the issue better this way |
Thanks that should help! My understanding is that a swagger spec will always have a .json or a .yml/.yaml extension, do you know if that is correct? |
Yes, quote from the website
I did some further digging. It really has to do sth with the badge format .json at the end is always interpreted as the badge format not as part of the url |
Okay I managed to get this working, though with one caveat that IMO we'll need to accept. I'd started making some local changes so the service class diverged just a bit from what you have here. Feel free to incorporate or toss, but something like this should get us to a point of having this solved. swagger.service.js'use strict'
const Joi = require('@hapi/joi')
const { optionalUrl } = require('../validators')
const { BaseJsonService, NotFound } = require('..')
const schema = Joi.object({
schemaValidationMessages: Joi.array().items(
Joi.object({
level: Joi.string().required(),
message: Joi.string().required(),
}).required()
),
}).required()
const queryParamSchema = Joi.object({
specUrl: optionalUrl.required(),
}).required()
module.exports = class SwaggerValidatorService extends BaseJsonService {
static get category() {
return 'other'
}
static get route() {
return {
base: 'swagger/valid',
pattern: '2.0',
queryParamSchema,
}
}
static get examples() {
return [
{
title: 'Swagger Validator',
staticPreview: this.render({ status: 'valid' }),
namedParams: {},
queryParams: {
specUrl:
'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore-expanded.json',
},
},
]
}
static get defaultBadgeData() {
return { label: 'swagger' }
}
static render({ status }) {
if (status === 'valid') {
return { message: status, color: 'brightgreen' }
} else {
return { message: status, color: 'red' }
}
}
async fetch({ specUrl }) {
return this._requestJson({
url: 'http://validator.swagger.io/validator/debug',
schema,
options: { qs: { url: specUrl } },
})
}
transform({ json, specUrl }) {
const valMessages = json.schemaValidationMessages
if (!valMessages || valMessages.length === 0) {
return { status: 'valid' }
}
if (valMessages.length === 1) {
const { message, level } = valMessages[0]
if (level === 'error' && message === `Can't read from file ${specUrl}`) {
throw new NotFound({ prettyMessage: 'spec not found or unreadable' })
}
}
if (valMessages.every(msg => msg.level === 'warning')) {
return { status: 'valid' }
}
return { status: 'invalid' }
}
async handle(_routeParams, { specUrl }) {
const json = await this.fetch({ specUrl })
const { status } = this.transform({ json, specUrl })
return this.constructor.render({ status })
}
} swagger-redirect.service.js'use strict'
const { redirector } = require('..')
module.exports = [
redirector({
category: 'other',
name: 'SwaggerRedirect',
route: {
base: 'swagger/valid/2.0',
pattern: ':scheme(https|http)/:url*',
},
transformPath: () => '/swagger/valid/2.0',
transformQueryParams: ({ scheme, url }) => {
const suffix = /(yaml|yml|json)$/.test(url) ? '' : '.json'
return { specUrl: `${scheme}://${url}${suffix}` }
},
dateAdded: new Date('2019-11-03'),
}),
] Currently, no one is able to use the Swagger badge with the default/extensionless mode when using a json-based spec. So this doesn't actually work, as the filename is misinterpreted as the requested {"label":"swagger","message":"invalid","color":"red","link":[],"name":"swagger","value":"invalid"} Shields users would have to explicitly include the Using the approach I've shared above, this edge case (json-based spec without explicit badge extension) is still providing a json response, but at least now it is the correct json response. {"label":"swagger","message":"valid","color":"brightgreen","link":[],"name":"swagger","value":"valid"} I do not see a way to magically correct that original edge case as part of the redirector, so I believe this the best we can do. We knew that there would be a handful of edge cases that would be problematic when we updated the badge formats to Everything works fine when explicitly including the extension with the legacy routes, and everything will work fine going forward (since the url of the spec has been moved to the query param) |
Also, I'd love to actually get some live service tests for this. If there's no publicly accessible specs that are relatively consistent (ones that will be valid or invalid for an extended duration) then we can have the tests point to a live spec with an assertion that uses We can also add some tests for the not found scenario now that we're properly detecting them. |
Now json should be usable with json format by default, svg needs to be specified in addition just as you said. I also added live tests and tests for the redirector. |
services/swagger/swagger.tester.js
Outdated
@@ -21,6 +24,27 @@ t.create('Valid') | |||
color: 'brightgreen', | |||
}) | |||
|
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 remove any of the mocked tests that are duplicative of the real/live tests. You acn also remove the Live
prefix from the test names, as our test runner automatically denotes whether a test is mocked or live in the output
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 removed all mocked ones besides the invalid one
services/swagger/swagger.tester.js
Outdated
}) | ||
|
||
// Isn't a spec, but valid json | ||
t.create('Live invalid') |
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.
One live test I'm not sure though: The invalid spec I just used the schema for a spec, but its not a spec as such. (but valid json)
I'm guessing this is the test you were referring to?
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 exactly
color: 'brightgreen', | ||
}) | ||
|
||
// Isn't a spec, but valid 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.
I think this is fine, as it still allows us to validate our integration with/handling of the response from the validator API 👍
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.
Thanks so much for this! There were a lot of unexpected additional items that were needed to fix this, so really appreciate you working through them all!
If the official example from here is used against the swagger Validator it generates a valid badge, but does also generate some warning level errors. (See valid badge and debug messages)
I adjusted the parsing of the endjust endpoint so warnings don't generate an invalid swagger Spec.
I also added a test for this scenario.