Skip to content

Commit

Permalink
Merge pull request swagger-api#2 from shah-vidhi/feature/copy-to-clip…
Browse files Browse the repository at this point in the history
…board

changes for copy-to-clipboard
  • Loading branch information
shah-vidhi authored Jan 15, 2020
2 parents 604c7b4 + 7e32352 commit 020c95b
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 5 deletions.
31 changes: 31 additions & 0 deletions src/core/components/copy-component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react"
import PropTypes from "prop-types"

function copyToClipboard(e, getBaseUrl, path, method) {
const baseUrl = document.createElement("input")
baseUrl.setAttribute("value", getBaseUrl(path, method))
baseUrl.addEventListener("copy", () => e.stopPropagation())
document.body.appendChild(baseUrl)
baseUrl.select()
document.execCommand("copy")
document.body.removeChild(baseUrl)
}

const CopyComponent = ({ getBaseUrl, path, method }) => {

return (
<div onClick={e => copyToClipboard(e, getBaseUrl, path, method)}>
<svg width="20" height="17">
<use xlinkHref={"#copy-to-clipboard"} />
</svg>
</div>
)
}

CopyComponent.propTypes = {
getBaseUrl: PropTypes.func.isRequired,
path: PropTypes.object.isRequired,
method: PropTypes.object.isRequired
}

export default CopyComponent
2 changes: 1 addition & 1 deletion src/core/components/curl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class Curl extends React.Component {
return (
<div>
<h4>Curl</h4>
<div className="copy-paste">
<div className="copy-paste fs-block">
<textarea onFocus={this.handleFocus} readOnly="true" className="curl" style={{ whiteSpace: "normal" }} value={curl}></textarea>
</div>
</div>
Expand Down
7 changes: 6 additions & 1 deletion src/core/components/operation-summary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from "prop-types"
import { Iterable, List } from "immutable"
import ImPropTypes from "react-immutable-proptypes"
import toString from "lodash/toString"
import CopyComponent from "./copy-component"


export default class OperationSummary extends PureComponent {
Expand All @@ -15,6 +16,7 @@ export default class OperationSummary extends PureComponent {
getConfigs: PropTypes.func.isRequired,
authActions: PropTypes.object,
authSelectors: PropTypes.object,
getBaseUrl: PropTypes.func.isRequired
}

static defaultProps = {
Expand All @@ -32,6 +34,7 @@ export default class OperationSummary extends PureComponent {
authSelectors,
operationProps,
specPath,
getBaseUrl
} = this.props

let {
Expand All @@ -43,6 +46,7 @@ export default class OperationSummary extends PureComponent {
operationId,
originalOperationId,
displayOperationId,
path
} = operationProps.toJS()

let {
Expand Down Expand Up @@ -80,9 +84,10 @@ export default class OperationSummary extends PureComponent {
}}
/>
}
<CopyComponent getBaseUrl={getBaseUrl} path={path} method={method}/>
<JumpToPath path={specPath} />{/* TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}
</div>
)

}
}
}
20 changes: 17 additions & 3 deletions src/core/components/operation.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { getList } from "core/utils"
import { getExtensions, sanitizeUrl, escapeDeepLinkPath } from "core/utils"
import { getExtensions, sanitizeUrl, escapeDeepLinkPath, baseUrl } from "core/utils"
import { Iterable, List } from "immutable"
import ImPropTypes from "react-immutable-proptypes"

Expand Down Expand Up @@ -110,9 +110,23 @@ export default class Operation extends PureComponent {

let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )

const getBaseUrl = (path, method) => {
const obj = {
spec: specSelectors.specJson().toJS(),
contextUrl: specSelectors.url(),
specIsOAS3: specSelectors.isOAS3(specSelectors.specJson().toJS()),
server: oas3Selectors.selectedServer(),
pathName: path,
method,
scheme: specSelectors.operationScheme()
}
return baseUrl(obj)
}

return (
<div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={escapeDeepLinkPath(isShownKey.join("-"))} >
<OperationSummary operationProps={operationProps} toggleShown={toggleShown} getComponent={getComponent} authActions={authActions} authSelectors={authSelectors} specPath={specPath} />
<OperationSummary operationProps={operationProps} toggleShown={toggleShown} getComponent={getComponent} authActions={authActions} authSelectors={authSelectors} specPath={specPath}
getBaseUrl={getBaseUrl}/>
<Collapse isOpened={isShown}>
<div className="opblock-body">
{ (operation && operation.size) || operation === null ? null :
Expand Down Expand Up @@ -233,4 +247,4 @@ export default class Operation extends PureComponent {
)
}

}
}
4 changes: 4 additions & 0 deletions src/core/components/svg-assets.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const SvgAssets = () =>
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>

<symbol viewBox="0 0 24 24" id="copy-to-clipboard">
<path fill="#808080" d="M21.961,5.308c-0.025-0.061-0.062-0.116-0.108-0.162l-4.999-4.999c-0.046-0.045-0.101-0.083-0.162-0.107 C16.632,0.014,16.567,0,16.5,0h-9C7.224,0,7,0.224,7,0.5V3H1.5C1.224,3,1,3.224,1,3.5v20C1,23.776,1.224,24,1.5,24h14 c0.276,0,0.5-0.224,0.5-0.5V21h5.5c0.276,0,0.5-0.224,0.5-0.5v-15C22,5.433,21.986,5.368,21.961,5.308z M17,1.707L20.293,5H17 V1.707z M15,23H2V4h8v4.5C10,8.776,10.224,9,10.5,9H15V23z M14.293,8H11V4.707L14.293,8z M16,20V8.5 c0-0.067-0.014-0.132-0.039-0.192c-0.025-0.061-0.062-0.116-0.108-0.162l-4.999-4.999c-0.046-0.045-0.101-0.083-0.162-0.107 C10.632,3.014,10.567,3,10.5,3H8V1h8v4.5C16,5.776,16.224,6,16.5,6H21v14H16z"/>
</symbol>

</defs>
</svg>
</div>
Expand Down
107 changes: 107 additions & 0 deletions src/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import eq from "lodash/eq"
import { memoizedSampleFromSchema, memoizedCreateXMLExample } from "core/plugins/samples/fn"
import win from "./window"
import cssEscape from "css.escape"
import url from "url"
import getIn from "lodash/get"

const DEFAULT_RESPONSE_KEY = "default"

Expand Down Expand Up @@ -842,4 +844,109 @@ export function paramToValue(param, paramValues) {
.filter(value => value !== undefined)

return values[0]
}

const stripNonAlpha = str => (str ? str.replace(/\W/g, "") : null)

const buildOas3UrlWithContext = (ourUrl = "", contextUrl = "") => {
const parsedUrl = url.parse(ourUrl)
const parsedContextUrl = url.parse(contextUrl)

const computedScheme = stripNonAlpha(parsedUrl.protocol) || stripNonAlpha(parsedContextUrl.protocol) || ""
const computedHost = parsedUrl.host || parsedContextUrl.host
const computedPath = parsedUrl.pathname || ""
let res

if (computedScheme && computedHost) {
res = `${computedScheme}://${computedHost + computedPath}`

// If last character is '/', trim it off
}
else {
res = computedPath
}

return res[res.length - 1] === "/" ? res.slice(0, -1) : res
}

function getVariableTemplateNames(str) {
const results = []
const re = /{([^}]+)}/g
let text

// eslint-disable-next-line no-cond-assign
while (text = re.exec(str)) {
results.push(text[1])
}
return results
}

const oas3BaseUrl = ({ spec, pathName, method, server, contextUrl, serverVariables = {} }) => {
const servers =
getIn(spec, ["paths", pathName, (method || "").toLowerCase(), "servers"]) ||
getIn(spec, ["paths", pathName, "servers"]) ||
getIn(spec, ["servers"])

let selectedServerUrl = ""
let selectedServerObj = null

if (server && servers && servers.length) {
const serverUrls = servers.map(srv => srv.url)

if (serverUrls.indexOf(server) > -1) {
selectedServerUrl = server
selectedServerObj = servers[serverUrls.indexOf(server)]
}
}

if (!selectedServerUrl && servers && servers.length) {
// default to the first server if we don't have one by now
selectedServerUrl = servers[0].url
selectedServerObj = servers[0]
}

if (selectedServerUrl.indexOf("{") > -1) {
// do variable substitution
const varNames = getVariableTemplateNames(selectedServerUrl)
varNames.forEach((vari) => {
if (selectedServerObj.variables && selectedServerObj.variables[vari]) {
// variable is defined in server
const variableDefinition = selectedServerObj.variables[vari]
const variableValue = serverVariables[vari] || variableDefinition.default

const re = new RegExp(`{${vari}}`, "g")
selectedServerUrl = selectedServerUrl.replace(re, variableValue)
}
})
}

//return selectedServerUrl
return buildOas3UrlWithContext(selectedServerUrl, contextUrl)
}

const swagger2BaseUrl = ({ spec, scheme, contextUrl = "" }) => {
const parsedContextUrl = url.parse(contextUrl)
const firstSchemeInSpec = Array.isArray(spec.schemes) ? spec.schemes[0] : null

const computedScheme = scheme || firstSchemeInSpec || stripNonAlpha(parsedContextUrl.protocol) || "http"
const computedHost = spec.host || parsedContextUrl.host || ""
const computedPath = spec.basePath || ""
let res

if (computedScheme && computedHost) {
// we have what we need for an absolute URL
res = `${computedScheme}://${computedHost + computedPath}`
}
else {
// if not, a relative URL will have to do
res = computedPath
}

// If last character is '/', trim it off
return res[res.length - 1] === "/" ? res.slice(0, -1) : res
}

export const baseUrl = (obj) => {
const baseUrl = obj.specIsOAS3 ? oas3BaseUrl(obj) : swagger2BaseUrl(obj)
return (`${baseUrl}${obj.pathName}`)
}

0 comments on commit 020c95b

Please sign in to comment.