1
0
mirror of synced 2025-12-23 03:44:00 -05:00

account for non-object example values in rest docs (#33469)

Co-authored-by: Peter Bengtsson <peterbe@github.com>
This commit is contained in:
Rachael Sewell
2023-01-24 16:10:29 -08:00
committed by GitHub
parent 7c4833dbc9
commit 89e507215a
2 changed files with 117 additions and 42 deletions

View File

@@ -6,6 +6,16 @@ import { useVersion } from 'components/hooks/useVersion'
import { useMainContext } from 'components/context/MainContext'
type CodeExamples = Record<string, any>
// If the content type is application/x-www-form-urlencoded the format of
// the shell example is --data-urlencode param1=value1 --data-urlencode param2=value2
// For example, this operation:
// https://docs.github.com/en/enterprise/rest/reference/enterprise-admin#enable-or-disable-maintenance-mode
const CURL_CONTENT_TYPE_MAPPING: { [key: string]: string } = {
'application/x-www-form-urlencoded': '--data-urlencode',
'multipart/form-data': '--form',
'application/octet-stream': '--data-binary',
}
/*
Generates a curl example
@@ -19,10 +29,14 @@ type CodeExamples = Record<string, any>
export function getShellExample(operation: Operation, codeSample: CodeSample) {
const { currentVersion } = useVersion()
const { allVersions } = useMainContext()
// This allows us to display custom media types like application/sarif+json
const defaultAcceptHeader = codeSample?.response?.contentType?.includes('+json')
? codeSample.response.contentType
: 'application/vnd.github+json'
const defaultAcceptHeader = getAcceptHeader(codeSample)
// For operations that upload data using octet-stream, you need
// to explicitly set the content-type header.
const contentTypeHeader =
codeSample?.request?.contentType === 'application/octet-stream'
? '-H "Content-Type: application/octet-stream"'
: ''
let requestPath = codeSample?.request?.parameters
? parseTemplate(operation.requestPath).expand(codeSample.request.parameters)
@@ -38,22 +52,21 @@ export function getShellExample(operation: Operation, codeSample: CodeSample) {
"'\\''"
)}'`
// If the content type is application/x-www-form-urlencoded the format of
// the shell example is --data-urlencode param1=value1 --data-urlencode param2=value2
// For example, this operation:
// https://docs.github.com/en/enterprise/rest/reference/enterprise-admin#enable-or-disable-maintenance-mode
const CURL_CONTENT_TYPE_MAPPING: { [key: string]: string } = {
'application/x-www-form-urlencoded': '--data-urlencode',
'multipart/form-data': '--form',
}
const contentType = codeSample.request.contentType
if (codeSample.request.contentType in CURL_CONTENT_TYPE_MAPPING) {
if (contentType in CURL_CONTENT_TYPE_MAPPING) {
requestBodyParams = ''
const paramNames = Object.keys(codeSample.request.bodyParameters)
paramNames.forEach((elem) => {
requestBodyParams = `${requestBodyParams} ${CURL_CONTENT_TYPE_MAPPING[contentType]} "${elem}=${codeSample.request.bodyParameters[elem]}"`
})
// Most of the time the example body parameters have a name and value
// and are included in an object. But, some cases are a single value
// and the type is a string.
const { bodyParameters } = codeSample.request
if (bodyParameters && typeof bodyParameters === 'object' && !Array.isArray(bodyParameters)) {
const paramNames = Object.keys(bodyParameters)
paramNames.forEach((elem) => {
requestBodyParams = `${requestBodyParams} ${CURL_CONTENT_TYPE_MAPPING[contentType]} "${elem}=${bodyParameters[elem]}"`
})
} else {
requestBodyParams = `${CURL_CONTENT_TYPE_MAPPING[contentType]} "${bodyParameters}"`
}
}
}
@@ -71,6 +84,7 @@ export function getShellExample(operation: Operation, codeSample: CodeSample) {
const args = [
operation.verb !== 'get' && `-X ${operation.verb.toUpperCase()}`,
`-H "Accept: ${defaultAcceptHeader}" \\\n ${authHeader}${apiVersionHeader}`,
contentTypeHeader,
`${operation.serverUrl}${requestPath}`,
requestBodyParams,
].filter(Boolean)
@@ -88,9 +102,7 @@ export function getShellExample(operation: Operation, codeSample: CodeSample) {
-fref,topic-branch=0,payload,{ "deploy": "migrate" }=1,description,Deploy request from hubot=2
*/
export function getGHExample(operation: Operation, codeSample: CodeSample) {
const defaultAcceptHeader = codeSample?.response?.contentType?.includes('+json')
? codeSample.response.contentType
: 'application/vnd.github+json'
const defaultAcceptHeader = getAcceptHeader(codeSample)
const hostname = operation.serverUrl !== 'https://api.github.com' ? '--hostname HOSTNAME' : ''
let requestPath = codeSample?.request?.parameters
@@ -101,25 +113,33 @@ export function getGHExample(operation: Operation, codeSample: CodeSample) {
requestPath += requiredQueryParams ? `?${requiredQueryParams}` : ''
let requestBodyParams = ''
if (codeSample?.request?.bodyParameters) {
const bodyParamValues = Object.values(codeSample.request.bodyParameters)
// GitHub CLI does not support sending Objects and arrays using the -F or
// -f flags. That support may be added in the future. It is possible to
// use gh api --input to take a JSON object from standard input
// constructed by jq and piped to gh api. However, we'll hold off on adding
// that complexity for now.
if (bodyParamValues.some((elem) => typeof elem === 'object')) {
return undefined
// Most of the time the example body parameters have a name and value
// and are included in an object. But, some cases are a single value
// and the type is a string.
const { bodyParameters } = codeSample.request
if (bodyParameters) {
if (typeof bodyParameters === 'object' && !Array.isArray(bodyParameters)) {
const bodyParamValues = Object.values(codeSample.request.bodyParameters)
// GitHub CLI does not support sending Objects and arrays using the -F or
// -f flags. That support may be added in the future. It is possible to
// use gh api --input to take a JSON object from standard input
// constructed by jq and piped to gh api. However, we'll hold off on adding
// that complexity for now.
if (bodyParamValues.some((elem) => typeof elem === 'object')) {
return undefined
}
requestBodyParams = Object.keys(codeSample.request.bodyParameters)
.map((key) => {
if (typeof codeSample.request.bodyParameters[key] === 'string') {
return `-f ${key}='${codeSample.request.bodyParameters[key]}' `
} else {
return `-F ${key}=${codeSample.request.bodyParameters[key]} `
}
})
.join('\\\n ')
} else {
requestBodyParams = `-f '${codeSample.request.bodyParameters}'`
}
requestBodyParams = Object.keys(codeSample.request.bodyParameters)
.map((key) => {
if (typeof codeSample.request.bodyParameters[key] === 'string') {
return `-f ${key}='${codeSample.request.bodyParameters[key]}' `
} else {
return `-F ${key}=${codeSample.request.bodyParameters[key]} `
}
})
.join('\\\n ')
}
const args = [
operation.verb !== 'get' && `--method ${operation.verb.toUpperCase()}`,
@@ -147,9 +167,21 @@ export function getGHExample(operation: Operation, codeSample: CodeSample) {
*/
export function getJSExample(operation: Operation, codeSample: CodeSample) {
const parameters = codeSample.request
? { ...codeSample.request.parameters, ...codeSample.request.bodyParameters }
: {}
const parameters: { [key: string]: string } = {}
if (codeSample.request) {
Object.assign(parameters, codeSample.request.parameters)
// Most of the time the example body parameters have a name and value
// and are included in an object. But, some cases are a single value
// and the type is a string.
if (
codeSample.request.bodyParameters &&
typeof codeSample.request.bodyParameters !== 'object'
) {
parameters.data = codeSample.request.bodyParameters
} else {
Object.assign(parameters, codeSample.request.bodyParameters)
}
}
let queryParameters = ''
@@ -230,3 +262,10 @@ function getRequiredQueryParamsPath(operation: Operation, codeSample: CodeSample
return requiredQueryParams.toString()
}
function getAcceptHeader(codeSample: CodeSample) {
// This allows us to display custom media types like application/sarif+json
return codeSample?.response?.contentType?.includes('+json')
? codeSample.response.contentType
: 'application/vnd.github+json'
}