requestHeader: byRole('list', { name: 'api_documentation.v2.request_subheader.header' }).byRole(
'listitem',
),
+ requestBodyParameter: byRole('list', {
+ name: 'api_documentation.v2.request_subheader.request_body',
+ }).byRole('listitem'),
response: byRole('list', { name: 'api_documentation.v2.response_header' }).byRole('listitem'),
};
expect(ui.pathParameter.query()).not.toBeInTheDocument();
expect(ui.requestHeader.query()).not.toBeInTheDocument();
expect(ui.requestBody.get()).toBeInTheDocument();
+ expect(ui.requestBodyParameter.getAll()).toHaveLength(6);
+ expect(ui.requestBodyParameter.byRole('button').getAt(0)).toHaveAttribute(
+ 'aria-expanded',
+ 'false',
+ );
+ await user.click(ui.requestBodyParameter.byRole('button').getAt(0));
+ expect(ui.requestBodyParameter.byRole('button').getAt(0)).toHaveAttribute(
+ 'aria-expanded',
+ 'true',
+ );
+ expect(ui.requestBodyParameter.getAt(0)).toHaveTextContent('name requiredmax: 100min: 3');
+ await user.click(ui.requestBodyParameter.byRole('button').getAt(0));
+ expect(ui.requestBodyParameter.byRole('button').getAt(0)).toHaveAttribute(
+ 'aria-expanded',
+ 'false',
+ );
expect(ui.response.byRole('button').getAt(0)).toHaveAttribute('aria-expanded', 'true');
expect(ui.response.byRole('button').getAt(2)).toHaveAttribute('aria-expanded', 'false');
expect(ui.response.getAt(0)).toHaveTextContent('200Successful operation');
expect(ui.queryParameter.getAt(1)).not.toHaveTextContent('deprecated');
await user.click(ui.queryParameter.byRole('button').getAt(1));
expect(ui.queryParameter.getAt(1)).toHaveTextContent('max: 5min: -1example: 3');
+
+ await user.click(ui.apiSidebarItem.getAt(7));
+ expect(await screen.findByText('/api/v3/pet/{petId}/uploadImage')).toBeInTheDocument();
+ expect(screen.getByText('no_data')).toBeInTheDocument();
});
it('should show About page', async () => {
import { translate } from '../../../helpers/l10n';
import { ExcludeReferences } from '../types';
import { mapOpenAPISchema } from '../utils';
+import ApiRequestBodyParameters from './ApiRequestParameters';
import ApiResponseSchema from './ApiResponseSchema';
interface Props {
return (
<>
<SubTitle>{translate('api_documentation.v2.parameter_header')}</SubTitle>
- {!requestBody && !data.parameters?.length && <TextMuted text={translate('no_data')} />}
- {requestBody && (
- <div>
- <SubHeading>
- {translate('api_documentation.v2.request_subheader.request_body')}
- </SubHeading>
- <ApiResponseSchema content={requestBody} />
- </div>
- )}
{Object.entries(groupBy(data.parameters, (p) => p.in)).map(([group, parameters]) => (
<div key={group}>
<SubHeading id={`api-parameters-${group}`}>
text={`${translate('max')}: ${parameter.schema?.maximum}`}
/>
)}
- {parameter.schema?.minimum && (
+ {typeof parameter.schema?.minimum === 'number' && (
<TextMuted
className="sw-mt-2 sw-block"
text={`${translate('min')}: ${parameter.schema?.minimum}`}
</ul>
</div>
))}
+ {!requestBody && !data.parameters?.length && <TextMuted text={translate('no_data')} />}
+ {requestBody && (
+ <div>
+ <SubHeading id="api_documentation.v2.request_subheader.request_body">
+ {translate('api_documentation.v2.request_subheader.request_body')}
+ </SubHeading>
+ <ApiResponseSchema content={requestBody} />
+ <ApiRequestBodyParameters content={requestBody} />
+ </div>
+ )}
</>
);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { Accordion, Badge, TextMuted } from 'design-system';
+import { isEmpty } from 'lodash';
+import { OpenAPIV3 } from 'openapi-types';
+import React from 'react';
+import { translate } from '../../../helpers/l10n';
+import { ExcludeReferences } from '../types';
+
+interface Props {
+ content?: Exclude<ExcludeReferences<OpenAPIV3.ResponseObject>['content'], undefined>;
+}
+
+export default function ApiRequestBodyParameters({ content }: Readonly<Props>) {
+ const [openParameters, setOpenParameters] = React.useState<string[]>([]);
+
+ const toggleParameter = (parameter: string) => {
+ if (openParameters.includes(parameter)) {
+ setOpenParameters(openParameters.filter((n) => n !== parameter));
+ } else {
+ setOpenParameters([...openParameters, parameter]);
+ }
+ };
+
+ const schema =
+ content &&
+ (content['application/json']?.schema || content['application/merge-patch+json']?.schema);
+
+ if (!schema?.properties || schema?.type !== 'object' || isEmpty(schema?.properties)) {
+ return null;
+ }
+
+ const parameters = schema.properties;
+ const required = schema.required ?? [];
+
+ const orderedKeys = Object.keys(parameters).sort((a, b) => {
+ if (required?.includes(a) && !required?.includes(b)) {
+ return -1;
+ }
+ if (!required?.includes(a) && required?.includes(b)) {
+ return 1;
+ }
+ return 0;
+ });
+
+ return (
+ <ul aria-labelledby="api_documentation.v2.request_subheader.request_body">
+ {orderedKeys.map((key) => {
+ return (
+ <Accordion
+ className="sw-mt-2 sw-mb-4"
+ key={key}
+ header={
+ <div>
+ {key}{' '}
+ {schema.required?.includes(key) && (
+ <Badge className="sw-ml-2">{translate('required')}</Badge>
+ )}
+ {parameters[key].deprecated && (
+ <Badge variant="deleted" className="sw-ml-2">
+ {translate('deprecated')}
+ </Badge>
+ )}
+ </div>
+ }
+ data={key}
+ onClick={() => toggleParameter(key)}
+ open={openParameters.includes(key)}
+ >
+ <div>{parameters[key].description}</div>
+ {parameters[key].maxLength && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('max')}: ${parameters[key].maxLength}`}
+ />
+ )}
+ {typeof parameters[key].minLength === 'number' && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('min')}: ${parameters[key].minLength}`}
+ />
+ )}
+ {parameters[key].default !== undefined && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('default')}: ${parameters[key].default}`}
+ />
+ )}
+ </Accordion>
+ );
+ })}
+ </ul>
+ );
+}