Merge pull request #3292 from owenconti/ft/3135-request-duration

#3135 - Add request duration to response details
This commit is contained in:
shockey
2017-06-29 17:18:05 -07:00
committed by GitHub
7 changed files with 42 additions and 10 deletions

View File

@@ -143,6 +143,7 @@ parameterMacro | MUST be a function. Function to set default value to parameters
modelPropertyMacro | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable modelPropertyMacro | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'. docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'.
displayOperationId | Controls the display of operationId in operations list. The default is `false`. displayOperationId | Controls the display of operationId in operations list. The default is `false`.
displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`.
### Plugins ### Plugins

View File

@@ -8,25 +8,39 @@ const Headers = ( { headers } )=>{
<pre>{headers}</pre> <pre>{headers}</pre>
</div>) </div>)
} }
Headers.propTypes = { Headers.propTypes = {
headers: PropTypes.array.isRequired headers: PropTypes.array.isRequired
} }
const Duration = ( { duration } ) => {
return (
<div>
<h5>Request duration</h5>
<pre>{duration} ms</pre>
</div>
)
}
Duration.propTypes = {
duration: PropTypes.number.isRequired
}
export default class LiveResponse extends React.Component { export default class LiveResponse extends React.Component {
static propTypes = { static propTypes = {
response: PropTypes.object.isRequired, response: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired getComponent: PropTypes.func.isRequired,
displayRequestDuration: PropTypes.bool.isRequired
} }
render() { render() {
const { request, response, getComponent } = this.props const { request, response, getComponent, displayRequestDuration } = this.props
const status = response.get("status") const status = response.get("status")
const url = response.get("url") const url = response.get("url")
const headers = response.get("headers").toJS() const headers = response.get("headers").toJS()
const notDocumented = response.get("notDocumented") const notDocumented = response.get("notDocumented")
const isError = response.get("error") const isError = response.get("error")
const duration = response.get("duration")
const body = isError ? response.get("response").get("text") : response.get("text") const body = isError ? response.get("response").get("text") : response.get("text")
@@ -80,6 +94,9 @@ export default class LiveResponse extends React.Component {
{ {
hasHeaders ? <Headers headers={ returnObject }/> : null hasHeaders ? <Headers headers={ returnObject }/> : null
} }
{
displayRequestDuration && duration ? <Duration duration={ duration } /> : null
}
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@@ -17,6 +17,7 @@ export default class Operation extends PureComponent {
allowTryItOut: PropTypes.bool, allowTryItOut: PropTypes.bool,
displayOperationId: PropTypes.bool, displayOperationId: PropTypes.bool,
displayRequestDuration: PropTypes.bool,
response: PropTypes.object, response: PropTypes.object,
request: PropTypes.object, request: PropTypes.object,
@@ -37,6 +38,7 @@ export default class Operation extends PureComponent {
response: null, response: null,
allowTryItOut: true, allowTryItOut: true,
displayOperationId: false, displayOperationId: false,
displayRequestDuration: false
} }
constructor(props, context) { constructor(props, context) {
@@ -107,7 +109,7 @@ export default class Operation extends PureComponent {
request, request,
allowTryItOut, allowTryItOut,
displayOperationId, displayOperationId,
displayRequestDuration,
fn, fn,
getComponent, getComponent,
specActions, specActions,
@@ -252,6 +254,7 @@ export default class Operation extends PureComponent {
produces={ produces } produces={ produces }
producesValue={ operation.get("produces_value") } producesValue={ operation.get("produces_value") }
pathMethod={ [path, method] } pathMethod={ [path, method] }
displayRequestDuration={ displayRequestDuration }
fn={fn} /> fn={fn} />
} }
</div> </div>

View File

@@ -32,7 +32,7 @@ export default class Operations extends React.Component {
const Collapse = getComponent("Collapse") const Collapse = getComponent("Collapse")
let showSummary = layoutSelectors.showSummary() let showSummary = layoutSelectors.showSummary()
let { docExpansion, displayOperationId } = getConfigs() let { docExpansion, displayOperationId, displayRequestDuration } = getConfigs()
return ( return (
<div> <div>
@@ -87,6 +87,7 @@ export default class Operations extends React.Component {
allowTryItOut={allowTryItOut} allowTryItOut={allowTryItOut}
displayOperationId={displayOperationId} displayOperationId={displayOperationId}
displayRequestDuration={displayRequestDuration}
specActions={ specActions } specActions={ specActions }
specSelectors={ specSelectors } specSelectors={ specSelectors }

View File

@@ -14,19 +14,21 @@ export default class Responses extends React.Component {
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired, pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
fn: PropTypes.object.isRequired fn: PropTypes.object.isRequired
} }
static defaultProps = { static defaultProps = {
request: null, request: null,
tryItOutResponse: null, tryItOutResponse: null,
produces: fromJS(["application/json"]) produces: fromJS(["application/json"]),
displayRequestDuration: false
} }
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val) onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
render() { render() {
let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue } = this.props let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue, displayRequestDuration } = this.props
let defaultCode = defaultStatusCode( responses ) let defaultCode = defaultStatusCode( responses )
const ContentType = getComponent( "contentType" ) const ContentType = getComponent( "contentType" )
@@ -53,7 +55,8 @@ export default class Responses extends React.Component {
: <div> : <div>
<LiveResponse request={ request } <LiveResponse request={ request }
response={ tryItOutResponse } response={ tryItOutResponse }
getComponent={ getComponent } /> getComponent={ getComponent }
displayRequestDuration={ displayRequestDuration } />
<h4>Responses</h4> <h4>Responses</h4>
</div> </div>

View File

@@ -8,7 +8,7 @@ import { parseSeach, filterConfigs } from "core/utils"
const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion",
"apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl", "apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl",
"showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" ] "showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" , "displayRequestDuration"]
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo
@@ -30,6 +30,7 @@ module.exports = function SwaggerUI(opts) {
configs: {}, configs: {},
custom: {}, custom: {},
displayOperationId: false, displayOperationId: false,
displayRequestDuration: false,
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance. // Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest. // Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.

View File

@@ -207,8 +207,14 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
specActions.setRequest(req.pathName, req.method, parsedRequest) specActions.setRequest(req.pathName, req.method, parsedRequest)
// track duration of request
const startTime = Date.now()
return fn.execute(req) return fn.execute(req)
.then( res => specActions.setResponse(req.pathName, req.method, res)) .then( res => {
res.duration = Date.now() - startTime
specActions.setResponse(req.pathName, req.method, res)
} )
.catch( err => specActions.setResponse(req.pathName, req.method, { error: true, err: serializeError(err) } ) ) .catch( err => specActions.setResponse(req.pathName, req.method, { error: true, err: serializeError(err) } ) )
} }