From f9185c6a3c927d4c1edbeb0a5cc865adc19a48dc Mon Sep 17 00:00:00 2001 From: Timon Rey Turak <31767369+timonrey@users.noreply.github.com> Date: Wed, 16 Aug 2023 17:27:42 +0200 Subject: [PATCH] Display content types for responses correctly (#1790) * fix: display content types for responses correctly * feat: enable other content types for responses * chore: fix graphql query * chore: show no content type if description is defined * chore: add testcase for a different content type for responses * chore: add changeset * chore: fix indentation * refactor: add safety checks * chore: show content type only if description is not displayed * refactor: rename prop --- .changeset/witty-glasses-share.md | 6 +++ api-specs/test/api.raml | 17 ++++++-- .../src/components/resource/method/method.js | 33 +++++++++++---- .../method/request-response-examples.js | 21 ++++------ .../components/resource/method/responses.js | 33 ++++++++++----- .../src/hooks/use-api-resources.js | 40 +++++++++++++++---- .../src/utils/render-type-as-link.js | 12 +----- .../src/utils/resource/process-methods.mjs | 12 ++++-- 8 files changed, 116 insertions(+), 58 deletions(-) create mode 100644 .changeset/witty-glasses-share.md diff --git a/.changeset/witty-glasses-share.md b/.changeset/witty-glasses-share.md new file mode 100644 index 0000000000..5e85da67f0 --- /dev/null +++ b/.changeset/witty-glasses-share.md @@ -0,0 +1,6 @@ +--- +'@commercetools-docs/gatsby-transformer-raml': minor +'@commercetools-docs/gatsby-theme-api-docs': minor +--- + +Display the response content type correctly. It also enables support for other content types that are already supported in request bodies. diff --git a/api-specs/test/api.raml b/api-specs/test/api.raml index 4510938412..4748300e2e 100644 --- a/api-specs/test/api.raml +++ b/api-specs/test/api.raml @@ -284,10 +284,11 @@ uses: 203: description: ArrayTestType using 2 different custom example files body: - type: arrays.ArrayTestType - examples: - custom1: !include examples/array-test-type-custom-1.json - custom2: !include examples/array-test-type-custom-2.json + application/x-www-form-urlencoded: + type: arrays.ArrayTestType + examples: + custom1: !include examples/array-test-type-custom-1.json + custom2: !include examples/array-test-type-custom-2.json /description-with-urn-links: post: @@ -338,6 +339,14 @@ uses: body: application/x-www-form-urlencoded: type: scalars.StringTestType + responses: + 200: + body: + application/json: + type: scalars.StringTestType + 201: + body: + type: date-test-type.DateTestType /searchApiPattern: get: diff --git a/packages/gatsby-theme-api-docs/src/components/resource/method/method.js b/packages/gatsby-theme-api-docs/src/components/resource/method/method.js index 138c9e96af..a1b32957f6 100644 --- a/packages/gatsby-theme-api-docs/src/components/resource/method/method.js +++ b/packages/gatsby-theme-api-docs/src/components/resource/method/method.js @@ -33,7 +33,7 @@ const Container = styled.div` const TitleWithAnchor = Markdown.withCopyToClipboard(Title); -function convertContentType(type) { +export function convertContentType(type) { switch (type) { case 'applicationjson': return 'application/json'; @@ -67,7 +67,9 @@ const Method = ({ allUriParameters = allUriParameters.concat(method.uriParameters); } - const contentType = []; + const requestContentType = []; + const responseContentType = []; + if (method.body) { const findOutContentTypes = Object.keys(method.body).reduce( (list, value) => { @@ -76,13 +78,29 @@ const Method = ({ [] ); findOutContentTypes.forEach((type) => { - contentType.push(convertContentType(type)); + requestContentType.push(convertContentType(type)); + }); + } + + if (method.responses) { + const findOutContentTypes = []; + method.responses.forEach((response) => { + response.body && + Object.keys(response.body).forEach((key) => { + if (convertContentType(key) !== '' && response.body[key]) { + findOutContentTypes.push(key); + } + }); + }); + + findOutContentTypes.forEach((type) => { + responseContentType.push(type); }); } const isStructuredDataType = - contentType.includes('application/json') || - contentType.includes('application/x-www-form-urlencoded'); + requestContentType.includes('application/json') || + requestContentType.includes('application/x-www-form-urlencoded'); const methodColor = computeMethodColor(methodType.toLowerCase()); @@ -144,7 +162,7 @@ const Method = ({ method.body.applicationxwwwformurlencoded?.type } isStructuredDataType={isStructuredDataType} - contentType={contentType} + contentType={requestContentType} /> )} @@ -152,7 +170,7 @@ const Method = ({ )} @@ -161,6 +179,7 @@ const Method = ({ apiKey={apiKey} requestCodeExamples={method.codeExamples} responses={method.responses} + contentType={responseContentType} /> )} diff --git a/packages/gatsby-theme-api-docs/src/components/resource/method/request-response-examples.js b/packages/gatsby-theme-api-docs/src/components/resource/method/request-response-examples.js index 07c00102b7..15093b4063 100644 --- a/packages/gatsby-theme-api-docs/src/components/resource/method/request-response-examples.js +++ b/packages/gatsby-theme-api-docs/src/components/resource/method/request-response-examples.js @@ -9,9 +9,8 @@ const RequestResponseExamples = (props) => { const apiTypes = useApiTypes(); const responsesCodeExamples = []; if (props.responses) { - props.responses.forEach((response) => { - const typeDisplayName = - response.body && response.body.applicationjson.type; + props.responses.forEach((response, index) => { + const typeDisplayName = response?.body?.[props.contentType[index]]?.type; const { code } = response; if (typeDisplayName) { const apiType = apiTypes.find((type) => { @@ -20,7 +19,8 @@ const RequestResponseExamples = (props) => { ); }); - const examplesNode = response.body?.applicationjson?.examples; + const examplesNode = + response.body?.[props.contentType[index]]?.examples; if (examplesNode && Array.isArray(examplesNode)) { examplesNode.forEach((example) => { @@ -33,7 +33,7 @@ const RequestResponseExamples = (props) => { value: example.value, }); }); - } else if (apiType.examples && apiType.examples.length > 0) { + } else if (apiType?.examples?.length > 0) { responsesCodeExamples.push({ code, typeDisplayName, @@ -84,17 +84,10 @@ RequestResponseExamples.propTypes = { responses: PropTypes.arrayOf( PropTypes.shape({ code: PropTypes.number.isRequired, - body: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.shape({ - applicationjson: PropTypes.shape({ - builtinType: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - }), - }), - ]), + body: PropTypes.object, }).isRequired ), + contentType: PropTypes.array.isRequired, }; export default RequestResponseExamples; diff --git a/packages/gatsby-theme-api-docs/src/components/resource/method/responses.js b/packages/gatsby-theme-api-docs/src/components/resource/method/responses.js index df2f580646..e2c1a0bd72 100644 --- a/packages/gatsby-theme-api-docs/src/components/resource/method/responses.js +++ b/packages/gatsby-theme-api-docs/src/components/resource/method/responses.js @@ -12,6 +12,7 @@ import SpacingsInline from '@commercetools-uikit/spacings-inline'; import { tokens, dimensions, typography } from '../../../design-system'; import { useTypeLocations } from '../../../hooks/use-type-locations'; import renderTypeAsLink from '../../../utils/render-type-as-link'; +import { convertContentType } from './method'; import Title from './title'; const ResponseCode = styled.span` @@ -35,7 +36,21 @@ const Responses = ({ apiKey, responses, contentType }) => { Response: - {responses.map((response) => { + {responses.map((response, index) => { + const convertedContentType = convertContentType(contentType[index]); + const responseDetails = + response.body && + renderTypeAsLink( + apiKey, + response.body[contentType[index]].type, + typeLocations, + convertedContentType + ); + // If renderTypeAsLink returns the type again and a description is defined, we display the description. + // In this case, the content type should not be displayed at the moment. + const showDescription = + responseDetails === response.body?.[contentType[index]].type && + response.description; return ( { {response.code} - {response.body ? ( + {responseDetails ? ( - {renderTypeAsLink( - apiKey, - response.body.applicationjson.type, - typeLocations, - response.description, - contentType - )} - {contentType.length > 0 && ( + {showDescription + ? markdownFragmentToReact(response.description) + : responseDetails} + {contentType.length > 0 && !showDescription && ( <> as - {contentType} + {convertedContentType} )} diff --git a/packages/gatsby-theme-api-docs/src/hooks/use-api-resources.js b/packages/gatsby-theme-api-docs/src/hooks/use-api-resources.js index 24156b4ec7..a09bb24bac 100644 --- a/packages/gatsby-theme-api-docs/src/hooks/use-api-resources.js +++ b/packages/gatsby-theme-api-docs/src/hooks/use-api-resources.js @@ -29,14 +29,7 @@ export const useApiResources = () => { code description body { - applicationjson { - type - builtinType - examples { - name - value - } - } + ...methodBodiesForResponses } } codeExamples { @@ -45,6 +38,37 @@ export const useApiResources = () => { } } + fragment methodBodiesForResponses on RamlResourceMethodBody { + applicationjson { + type + builtinType + examples { + name + value + } + } + applicationxwwwformurlencoded { + type + builtinType + examples { + name + value + } + } + imagejpeg { + type + builtinType + } + imagepng { + type + builtinType + } + imagegif { + type + builtinType + } + } + fragment methodBodies on RamlResourceMethodBody { applicationjson { type diff --git a/packages/gatsby-theme-api-docs/src/utils/render-type-as-link.js b/packages/gatsby-theme-api-docs/src/utils/render-type-as-link.js index bee3c7f86d..a87f644ac5 100644 --- a/packages/gatsby-theme-api-docs/src/utils/render-type-as-link.js +++ b/packages/gatsby-theme-api-docs/src/utils/render-type-as-link.js @@ -1,16 +1,10 @@ import React from 'react'; import { Link } from '@commercetools-docs/gatsby-theme-docs'; -import { markdownFragmentToReact, Markdown } from '@commercetools-docs/ui-kit'; +import { Markdown } from '@commercetools-docs/ui-kit'; import { locationForType } from '../hooks/use-type-locations'; import { getDescriptionIfPrimitiveType } from '../components/type/type'; -function renderTypeAsLink( - apiKey, - type, - typeLocations, - description, - contentType -) { +function renderTypeAsLink(apiKey, type, typeLocations, contentType) { const typeLocation = locationForType(apiKey, type, typeLocations); const originalTypeLocation = typeLocation ? typeLocation.url : undefined; @@ -28,8 +22,6 @@ function renderTypeAsLink( return {type}; } else if (primitiveJsonType) { return {primitiveJsonType}; - } else if (description) { - return markdownFragmentToReact(description); } return type; } diff --git a/packages/gatsby-transformer-raml/src/utils/resource/process-methods.mjs b/packages/gatsby-transformer-raml/src/utils/resource/process-methods.mjs index 84c45f59f4..08afd79859 100644 --- a/packages/gatsby-transformer-raml/src/utils/resource/process-methods.mjs +++ b/packages/gatsby-transformer-raml/src/utils/resource/process-methods.mjs @@ -37,10 +37,14 @@ function processMethods({ ); if (returnedMethods[method].responses) { returnedMethods[method].responses.forEach((response) => { - if (response?.body?.applicationjson.examples) { - response.body.applicationjson.examples = examplesToArray( - response.body.applicationjson.examples - ); + if (response?.body) { + Object.keys(response.body).forEach((key) => { + if (response.body[key].examples) { + response.body[key].examples = examplesToArray( + response.body[key].examples + ); + } + }); } }); }