Skip to content
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

Fix OAS3 relative urls #5341

Merged
merged 38 commits into from
Aug 4, 2020
Merged

Conversation

geraldglynn
Copy link
Contributor

OAS3 support relative URLs. These use the Server currently selected in the dropdown as the base.
This PR appends the relative URL to the selected Server

Description

Files modified:
src/core/containers/info.jsx
src/core/components/info.jsx
src/core/components/operations.jsx
src/core/components/operation-tag.jsx
src/core/components/operation.jsx

Motivation and Context

Currently relative URLs in OAS3 APIs render as relative to SPA URL; not the selected Server.

How Has This Been Tested?

This has been tested by running SwaggerUI locally and confirming URLs rendered correctly in:
Info
– Terms of service URL
– Contact URL
– License URL
– External Docs URL

Tag
– Tag External Docs URL

Operation
– Operation External Docs
-- Operation Tag

Screenshots (if appropriate):

Checklist

My PR contains...

  • No code changes (src/ is unmodified: changes to documentation, CI, metadata, etc.)
  • Dependency changes (any modification to dependencies in package.json)
  • Bug fixes (non-breaking change which fixes an issue)
  • Improvements (misc. changes to existing features)
  • Features (non-breaking change which adds functionality)

My changes...

  • are breaking changes to a public API (config options, System API, major UI change, etc).
  • are breaking changes to a private API (Redux, component props, utility functions, etc.).
  • are breaking changes to a developer API (npm script behavior changes, new dev system dependencies, etc).
  • are not breaking changes.

Documentation

  • My changes do not require a change to the project documentation.
  • My changes require a change to the project documentation.
  • If yes to above: I have updated the documentation accordingly.

Automated tests

  • My changes can not or do not need to be tested.
  • My changes can and should be tested by unit and/or integration tests.
  • If yes to above: I have added tests to cover my changes.
  • If yes to above: I have taken care to cover edge cases in my tests.
  • All new and existing tests passed.

@shockey shockey requested review from webron and shockey and removed request for webron May 3, 2019 21:17
@shockey
Copy link
Contributor

shockey commented May 3, 2019

@webron: thought this would need your review for a moment, but I now see that OpenAPI 3.0 is clear about Server Object URLs being the general source for relative URL bases. From there, it's not a stretch to assume we can use the "currently selected Server Object" specifically here.

Unless specified otherwise, all properties that are URLs MAY be relative references [...] relative references are resolved using the URLs defined in the Server Object as a Base URI.

source

shockey
shockey previously requested changes May 3, 2019
Copy link
Contributor

@shockey shockey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionality looks good - change requests and questions attached inline.

@@ -51,6 +52,7 @@ export default class OperationTag extends React.Component {
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
tagExternalDocsUrl = buildUrl( tagExternalDocsUrl, oas3Selectors.selectedServer() )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change request: would prefer to avoid reassignment here! some ideas:

  1. keep L54 and rename L55 to something like canonicalTagExternalDocsUrl
  2. rename L54 to rawTagExternalDocsUrl, keep L55

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have opted for option 2. rename L54 to rawTagExternalDocsUrl, keep L55
to avoid making changes further down in code

@@ -722,6 +722,19 @@ export function sanitizeUrl(url) {
return braintreeSanitizeUrl(url)
}

export function isAbsoluteUrl(url) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change request: new util functions need accompanying tests - ideally that would include cases for expected outputs, graceful handling of malformed inputs, and basic type safety (esp. undefined/null).

return url.match(/^(?:[a-z]+:)?\/\//i) // Matches http://, HTTP://, https://, ftp://, //example.com,
}

export function buildUrl(url="", selectedServer="") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change request: new util functions need accompanying tests - ideally that would include cases for expected outputs, graceful handling of malformed inputs, and basic type safety (esp. undefined/null).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not that I don't trust your code @geraldglynn - it's that I don't trust myself to remember all the edge cases relevant to this function when a first-time contributor opens a PR that touches it in 10 months 😄

render() {
const { url, getComponent } = this.props

const Link = getComponent("Link")

return <Link target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></Link>
return <Link target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } !!!</span></Link>
Copy link
Contributor

@shockey shockey May 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: is this an intentional change? Innovative....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No apologies

let version = info.get("version")
let description = info.get("description")
let title = info.get("title")
let termsOfService = info.get("termsOfService")
let termsOfService = buildUrl(info.get("termsOfService"), selectedServer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change request: can this be termsOfServiceUrl instead, for consistency with externalDocsUrl below? I know it wasn't introduced here but it would be nice 😄

let contact = info.get("contact")
let license = info.get("license")
const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
let { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
externalDocsUrl = buildUrl( externalDocs.url, selectedServer )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change request: would prefer to use ImmutableMap.getIn to set externalDocsUrl and externalDocsDescription, instead of futzing with toJS. Again, aware that this PR didn't introduce it, but since it's been touched let's improve it!

Would also like to avoid reassignment on externalDocsUrl - see my note on operation-tag.jsx L55, same idea here.

@shockey
Copy link
Contributor

shockey commented Jan 15, 2020

@geraldglynn — any idea if you'll be able to handle the change requests here? 🙂

@shockey
Copy link
Contributor

shockey commented Mar 1, 2020

closing due to inactivity!

@shockey shockey closed this Mar 1, 2020
@tim-lai tim-lai reopened this Jun 23, 2020
@geraldglynn geraldglynn requested a review from shockey July 21, 2020 14:39
@@ -1,6 +1,8 @@
function makeWindow() {
var win = {
location: {},
location: {
href: "https://app.swaggerhub.com/apis/smartbear/petstore/1.0.0##/pet/addPet"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this for unit testing servers with relative urls

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope this is just for draft purposes. You should be able to define a new window with custom data in Mocha.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the wrong approach to what I was trying to achieve, so probably better for to explain motivation...
In purpose of buildUrl():

  1. If URL is absolute, all is good; return the URL
  2. If URL is relative, then we want the base to be the Server URL
  3. However, if the Server URL itself is relative, we want the base to be the location of Definition YAML

In case #3 I was (incorrectly) setting the base URL to window location

Is there a way to get the location of the Definition YAML?
– I know what it is for our system, but it need to ben generic for Swagger-UI's system

Copy link
Contributor

@tim-lai tim-lai Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to get the location of the Definition YAML?

e.g. in petstore, you enter an external url path of a definition in the topbar? in that case, you can use spec.url
@geraldglynn

@geraldglynn geraldglynn requested a review from tim-lai July 27, 2020 10:50
@tim-lai
Copy link
Contributor

tim-lai commented Jul 28, 2020

please build

@tim-lai tim-lai dismissed shockey’s stale review July 29, 2020 01:50

changes implemented

@tim-lai
Copy link
Contributor

tim-lai commented Jul 29, 2020

@geraldglynn One last refactor request, then I think ready to merge... We've been wanting to split out the big utils.js file. So let's create a new file for these new utility functions. I'm proposing utils-oas3-urls.js, but open to something more generic or descriptive. Also separate out the unit tests into its own matching file. Thanks.

test/mocha/core/utils.js Outdated Show resolved Hide resolved
@tim-lai tim-lai merged commit d9f5691 into swagger-api:master Aug 4, 2020
let version = info.get("version")
let description = info.get("description")
let title = info.get("title")
let termsOfService = info.get("termsOfService")
let termsOfServiceUrl = buildUrl(info.get("termsOfService"), specUrl, {selectedServer})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a breaking change for us. Our OAS2 documents have inline termsOfService rather than a URL e.g.

termsOfService: 'Information in this document is subject to change ...'

Contrary to OAS 3 with OAS2 it is not required for the termsOfService to be a URL (as per https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-1). Hence, before building this URL the termsOfService value should first be verified to be a URL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants