* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { OpenAPIV3 } from 'openapi-types';
+import { InternalExtension } from '../../../apps/web-api-v2/types';
-export const openApiTestData: OpenAPIV3.Document<{ 'x-internal'?: 'true' }> = {
+export const openApiTestData: OpenAPIV3.Document<InternalExtension> = {
openapi: '3.0.2',
info: {
title: 'Swagger Petstore - OpenAPI 3.0',
},
},
},
+ '/test': {
+ get: {
+ summary: 'Test internal query params',
+ description: 'For tests only',
+ parameters: [
+ {
+ name: 'visible',
+ in: 'query',
+ description: 'parameter visible to anyone',
+ schema: { type: 'integer', format: 'int64' },
+ },
+ {
+ name: 'hidden',
+ in: 'query',
+ description: 'parameter is internal',
+ schema: { type: 'integer', format: 'int64' },
+ 'x-internal': 'true',
+ } as OpenAPIV3.ParameterObject & InternalExtension,
+ ],
+ responses: {
+ default: {
+ description: 'successful operation',
+ },
+ },
+ },
+ post: {
+ summary: 'Test internal query params',
+ description: 'For tests only',
+ requestBody: {
+ content: {
+ 'application/json': {
+ schema: {
+ type: 'object',
+ properties: {
+ visible: { type: 'string' },
+ hidden: { type: 'string', 'x-internal': 'true' } as OpenAPIV3.SchemaObject &
+ InternalExtension,
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ default: {
+ description: 'successful operation',
+ },
+ },
+ },
+ },
'/user': {
post: {
tags: ['user'],
import styled from '@emotion/styled';
import { LargeCenteredLayout, PageContentFontWrapper, Spinner, Title } from 'design-system';
import { omit } from 'lodash';
-import React, { useMemo } from 'react';
+import React, { useMemo, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { useLocation } from 'react-router-dom';
import { translate } from '../../helpers/l10n';
import { useOpenAPI } from '../../queries/web-api';
+import ApiFilterContext from './components/ApiFilterContext';
import ApiInformation from './components/ApiInformation';
import ApiSidebar from './components/ApiSidebar';
import { URL_DIVIDER, dereferenceSchema } from './utils';
export default function WebApiApp() {
+ const [showInternal, setShowInternal] = useState(false);
const { data, isLoading } = useOpenAPI();
const location = useLocation();
const activeApi = location.hash.replace('#', '').split(URL_DIVIDER);
activeApi.length > 1 &&
apis.find((api) => api.name === activeApi[0] && api.method === activeApi[1]);
+ const contextValue = useMemo(
+ () => ({
+ showInternal,
+ setShowInternal,
+ }),
+ [showInternal],
+ );
+
return (
- <LargeCenteredLayout>
- <PageContentFontWrapper className="sw-body-sm">
- <Helmet defer={false} title={translate('api_documentation.page')} />
- <Spinner loading={isLoading}>
- {data && (
- <div className="sw-w-full sw-flex">
- <NavContainer aria-label={translate('api_documentation.page')} className="sw--mx-2">
- <div className="sw-w-[300px] lg:sw-w-[390px] sw-mx-2">
- <ApiSidebar
- docInfo={data.info}
- apisList={apis.map(({ name, method, info }) => ({
- method,
- name,
- info,
- }))}
- />
- </div>
- </NavContainer>
- <main
- className="sw-relative sw-ml-12 sw-flex-1 sw-overflow-y-auto sw-py-6"
- style={{ height: 'calc(100vh - 160px)' }}
- >
- <Spinner loading={isLoading}>
- {!activeData && (
- <>
- <Title>{translate('about')}</Title>
- <p>{data.info.description}</p>
- </>
- )}
- {data && activeData && (
- <ApiInformation
- apiUrl={data.servers?.[0]?.url ?? ''}
- name={activeData.name}
- data={activeData.info}
- method={activeData.method}
+ <ApiFilterContext.Provider value={contextValue}>
+ <LargeCenteredLayout>
+ <PageContentFontWrapper className="sw-body-sm">
+ <Helmet defer={false} title={translate('api_documentation.page')} />
+ <Spinner loading={isLoading}>
+ {data && (
+ <div className="sw-w-full sw-flex">
+ <NavContainer aria-label={translate('api_documentation.page')} className="sw--mx-2">
+ <div className="sw-w-[300px] lg:sw-w-[390px] sw-mx-2">
+ <ApiSidebar
+ docInfo={data.info}
+ apisList={apis.map(({ name, method, info }) => ({
+ method,
+ name,
+ info,
+ }))}
/>
- )}
- </Spinner>
- </main>
- </div>
- )}
- </Spinner>
- </PageContentFontWrapper>
- </LargeCenteredLayout>
+ </div>
+ </NavContainer>
+ <main
+ className="sw-relative sw-ml-12 sw-flex-1 sw-overflow-y-auto sw-py-6"
+ style={{ height: 'calc(100vh - 160px)' }}
+ >
+ <Spinner loading={isLoading}>
+ {!activeData && (
+ <>
+ <Title>{translate('about')}</Title>
+ <p>{data.info.description}</p>
+ </>
+ )}
+ {data && activeData && (
+ <ApiInformation
+ apiUrl={data.servers?.[0]?.url ?? ''}
+ name={activeData.name}
+ data={activeData.info}
+ method={activeData.method}
+ />
+ )}
+ </Spinner>
+ </main>
+ </div>
+ )}
+ </Spinner>
+ </PageContentFontWrapper>
+ </LargeCenteredLayout>
+ </ApiFilterContext.Provider>
);
}
search: byRole('searchbox'),
title: byRole('link', { name: 'Swagger Petstore - OpenAPI 3.0 1.0.17' }),
searchClear: byRole('button', { name: 'clear' }),
- showInternal: byRole('checkbox', { name: 'api_documentation.show_internal' }),
+ showInternal: byRole('checkbox', { name: 'api_documentation.show_internal_v2' }),
apiScopePet: byRole('button', { name: 'pet' }),
apiScopeStore: byRole('button', { name: 'store' }),
apiScopeUser: byRole('button', { name: 'user' }),
+ apiScopeTest: byRole('button', { name: 'test' }),
+ publicButton: byRole('button', { name: /visible/ }),
+ internalButton: byRole('button', { name: /hidden/ }),
apiSidebarItem: byTestId('js-subnavigation-item'),
requestBody: byText('api_documentation.v2.request_subheader.request_body'),
queryParameter: byRole('list', { name: 'api_documentation.v2.request_subheader.query' }).byRole(
expect(ui.apiSidebarItem.getAll().length).toBeGreaterThan(3);
});
-it('should show internal', async () => {
+it('should show internal endpoints', async () => {
const user = userEvent.setup();
renderWebApiApp();
expect(await ui.apiScopeStore.find()).toBeInTheDocument();
.getAll()
.find((el) => el.textContent?.includes('internal'));
expect(internalItem).toBeInTheDocument();
- await user.click(internalItem!);
+ await user.click(internalItem as HTMLElement);
- expect(await screen.findByText('/api/v3/store/inventory')).toHaveTextContent(/internal/);
+ expect(await byRole('heading', { name: /\/api\/v3\/store\/inventory/ }).find()).toHaveTextContent(
+ /internal/,
+ );
+});
+
+it('should show internal parameters', async () => {
+ const user = userEvent.setup();
+ renderWebApiApp();
+ expect(await ui.apiScopeTest.find()).toBeInTheDocument();
+ await user.click(ui.apiScopeTest.get());
+ expect(ui.apiSidebarItem.getAll()).toHaveLength(2);
+
+ await user.click(
+ ui.apiSidebarItem.getAll().find((el) => el.textContent?.includes('GET')) as HTMLElement,
+ );
+ expect(await ui.publicButton.find()).toBeInTheDocument();
+ expect(ui.internalButton.query()).not.toBeInTheDocument();
+ await user.click(ui.showInternal.get());
+ expect(ui.publicButton.get()).toBeInTheDocument();
+ expect(ui.publicButton.get()).not.toHaveTextContent('internal');
+ expect(ui.internalButton.get()).toBeInTheDocument();
+ expect(ui.internalButton.get()).toHaveTextContent('internal');
+
+ await user.click(
+ ui.apiSidebarItem.getAll().find((el) => el.textContent?.includes('POST')) as HTMLElement,
+ );
+ expect(ui.publicButton.get()).toBeInTheDocument();
+ expect(ui.publicButton.get()).not.toHaveTextContent('internal');
+ expect(ui.internalButton.get()).toBeInTheDocument();
+ expect(ui.internalButton.get()).toHaveTextContent('internal');
+ await user.click(ui.showInternal.get());
+ expect(await ui.publicButton.find()).toBeInTheDocument();
+ expect(ui.internalButton.query()).not.toBeInTheDocument();
});
it('should navigate between apis', async () => {
--- /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 { Dispatch, SetStateAction, createContext } from 'react';
+
+const ApiFilterContext = createContext<{
+ showInternal: boolean;
+ setShowInternal: Dispatch<SetStateAction<boolean>>;
+}>({
+ showInternal: false,
+ setShowInternal: () => {},
+});
+
+export default ApiFilterContext;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
import { Badge, SubHeading, Title } from 'design-system';
import { OpenAPIV3 } from 'openapi-types';
import React from 'react';
import { translate } from '../../../helpers/l10n';
-import { ExcludeReferences } from '../types';
-import { getApiEndpointKey, getMethodClassName } from '../utils';
+import { ExcludeReferences, InternalExtension } from '../types';
+import { getApiEndpointKey } from '../utils';
import ApiParameters from './ApiParameters';
import ApiResponses from './ApiResponses';
+import RestMethodPill from './RestMethodPill';
interface Props {
- data: ExcludeReferences<OpenAPIV3.OperationObject<{ 'x-internal'?: 'true' }>>;
+ data: ExcludeReferences<OpenAPIV3.OperationObject<InternalExtension>>;
apiUrl: string;
name: string;
method: string;
<>
{data.summary && <Title>{data.summary}</Title>}
<SubHeading>
- <Badge className={classNames('sw-align-middle sw-mr-4', getMethodClassName(method))}>
- {method}
- </Badge>
- {apiUrl.replace(/.*(?=\/api)/, '') + name}
+ <RestMethodPill method={method} />
+ <span className="sw-ml-4">{apiUrl.replace(/.*(?=\/api)/, '') + name}</span>
{data['x-internal'] && (
<Badge variant="new" className="sw-ml-3">
{translate('internal')}
import { Accordion, Badge, SubHeading, SubTitle, TextMuted } from 'design-system';
import { groupBy } from 'lodash';
import { OpenAPIV3 } from 'openapi-types';
-import React from 'react';
+import React, { useContext } from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from '../../../helpers/l10n';
-import { ExcludeReferences } from '../types';
+import { ExcludeReferences, InternalExtension } from '../types';
import { mapOpenAPISchema } from '../utils';
+import ApiFilterContext from './ApiFilterContext';
import ApiRequestBodyParameters from './ApiRequestParameters';
import ApiResponseSchema from './ApiResponseSchema';
data: ExcludeReferences<OpenAPIV3.OperationObject>;
}
-export default function ApiParameters({ data }: Props) {
+export default function ApiParameters({ data }: Readonly<Props>) {
const [openParameters, setOpenParameters] = React.useState<string[]>([]);
+ const { showInternal } = useContext(ApiFilterContext);
const toggleParameter = (name: string) => {
if (openParameters.includes(name)) {
return (
<>
<SubTitle>{translate('api_documentation.v2.parameter_header')}</SubTitle>
- {Object.entries(groupBy(data.parameters, (p) => p.in)).map(([group, parameters]) => (
- <div key={group}>
- <SubHeading id={`api-parameters-${group}`}>
- {translate(`api_documentation.v2.request_subheader.${group}`)}
- </SubHeading>
- <ul aria-labelledby={`api-parameters-${group}`}>
- {parameters.map((parameter) => {
- return (
- <Accordion
- className="sw-mt-2 sw-mb-4"
- key={parameter.name}
- header={
- <div>
- {parameter.name}{' '}
- {parameter.schema && (
+ {Object.entries(groupBy(data.parameters, (p) => p.in)).map(
+ ([group, parameters]: [
+ string,
+ ExcludeReferences<Array<OpenAPIV3.ParameterObject & InternalExtension>>,
+ ]) => (
+ <div key={group}>
+ <SubHeading id={`api-parameters-${group}`}>
+ {translate(`api_documentation.v2.request_subheader.${group}`)}
+ </SubHeading>
+ <ul aria-labelledby={`api-parameters-${group}`}>
+ {parameters
+ .filter((parameter) => showInternal || !parameter['x-internal'])
+ .map((parameter) => {
+ return (
+ <Accordion
+ className="sw-mt-2 sw-mb-4"
+ key={parameter.name}
+ header={
+ <div>
+ {parameter.name}{' '}
+ {parameter.schema && (
+ <TextMuted
+ className="sw-inline sw-ml-2"
+ text={getSchemaType(parameter.schema)}
+ />
+ )}
+ {parameter.required && (
+ <Badge className="sw-ml-2">{translate('required')}</Badge>
+ )}
+ {parameter.deprecated && (
+ <Badge variant="deleted" className="sw-ml-2">
+ {translate('deprecated')}
+ </Badge>
+ )}
+ {parameter['x-internal'] && (
+ <Badge variant="new" className="sw-ml-2">
+ {translate('internal')}
+ </Badge>
+ )}
+ </div>
+ }
+ data={parameter.name}
+ onClick={toggleParameter}
+ open={openParameters.includes(parameter.name)}
+ >
+ <div>{parameter.description}</div>
+ {parameter.schema?.enum && (
+ <div className="sw-mt-2">
+ <FormattedMessage
+ id="api_documentation.v2.enum_description"
+ values={{
+ values: (
+ <div className="sw-body-sm-highlight">
+ {parameter.schema.enum.join(', ')}
+ </div>
+ ),
+ }}
+ />
+ </div>
+ )}
+ {parameter.schema?.maximum && (
<TextMuted
- className="sw-inline sw-ml-2"
- text={getSchemaType(parameter.schema)}
+ className="sw-mt-2 sw-block"
+ text={`${translate('max')}: ${parameter.schema?.maximum}`}
/>
)}
- {parameter.required && (
- <Badge className="sw-ml-2">{translate('required')}</Badge>
+ {typeof parameter.schema?.minimum === 'number' && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('min')}: ${parameter.schema?.minimum}`}
+ />
)}
- {parameter.deprecated && (
- <Badge variant="deleted" className="sw-ml-2">
- {translate('deprecated')}
- </Badge>
+ {parameter.example !== undefined && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('example')}: ${parameter.example}`}
+ />
)}
- </div>
- }
- data={parameter.name}
- onClick={toggleParameter}
- open={openParameters.includes(parameter.name)}
- >
- <div>{parameter.description}</div>
- {parameter.schema?.enum && (
- <div className="sw-mt-2">
- <FormattedMessage
- id="api_documentation.v2.enum_description"
- values={{
- values: (
- <div className="sw-body-sm-highlight">
- {parameter.schema.enum.join(', ')}
- </div>
- ),
- }}
- />
- </div>
- )}
- {parameter.schema?.maximum && (
- <TextMuted
- className="sw-mt-2 sw-block"
- text={`${translate('max')}: ${parameter.schema?.maximum}`}
- />
- )}
- {typeof parameter.schema?.minimum === 'number' && (
- <TextMuted
- className="sw-mt-2 sw-block"
- text={`${translate('min')}: ${parameter.schema?.minimum}`}
- />
- )}
- {parameter.example !== undefined && (
- <TextMuted
- className="sw-mt-2 sw-block"
- text={`${translate('example')}: ${parameter.example}`}
- />
- )}
- {parameter.schema?.default !== undefined && (
- <TextMuted
- className="sw-mt-2 sw-block"
- text={`${translate('default')}: ${parameter.schema?.default}`}
- />
- )}
- </Accordion>
- );
- })}
- </ul>
- </div>
- ))}
+ {parameter.schema?.default !== undefined && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('default')}: ${parameter.schema?.default}`}
+ />
+ )}
+ </Accordion>
+ );
+ })}
+ </ul>
+ </div>
+ ),
+ )}
{!requestBody && !data.parameters?.length && <TextMuted text={translate('no_data')} />}
{requestBody && (
<div>
import { Accordion, Badge, TextMuted } from 'design-system';
import { isEmpty } from 'lodash';
import { OpenAPIV3 } from 'openapi-types';
-import React from 'react';
+import React, { useContext } from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from '../../../helpers/l10n';
-import { ExcludeReferences } from '../types';
+import { ExcludeReferences, InternalExtension } from '../types';
+import ApiFilterContext from './ApiFilterContext';
interface Props {
- content?: Exclude<ExcludeReferences<OpenAPIV3.ResponseObject>['content'], undefined>;
+ content?: ExcludeReferences<OpenAPIV3.ResponseObject>['content'];
}
export default function ApiRequestBodyParameters({ content }: Readonly<Props>) {
const [openParameters, setOpenParameters] = React.useState<string[]>([]);
+ const { showInternal } = useContext(ApiFilterContext);
const toggleParameter = (parameter: string) => {
if (openParameters.includes(parameter)) {
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].enum && (
- <div className="sw-mt-2">
- <FormattedMessage
- id="api_documentation.v2.enum_description"
- values={{
- values: <i>{parameters[key].enum?.join(', ')}</i>,
- }}
+ {orderedKeys
+ .filter((key) => showInternal || !(parameters[key] as InternalExtension)['x-internal'])
+ .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>
+ )}
+ {parameters[key].deprecated && (
+ <Badge variant="deleted" className="sw-ml-2">
+ {translate('deprecated')}
+ </Badge>
+ )}
+ {(parameters[key] as InternalExtension)['x-internal'] && (
+ <Badge variant="new" className="sw-ml-2">
+ {translate('internal')}
+ </Badge>
+ )}
+ </div>
+ }
+ data={key}
+ onClick={() => toggleParameter(key)}
+ open={openParameters.includes(key)}
+ >
+ <div>{parameters[key].description}</div>
+ {parameters[key].enum && (
+ <div className="sw-mt-2">
+ <FormattedMessage
+ id="api_documentation.v2.enum_description"
+ values={{
+ values: <i>{parameters[key].enum?.join(', ')}</i>,
+ }}
+ />
+ </div>
+ )}
+ {parameters[key].maxLength && (
+ <TextMuted
+ className="sw-mt-2 sw-block"
+ text={`${translate('max')}: ${parameters[key].maxLength}`}
/>
- </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>
- );
- })}
+ )}
+ {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>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
import {
Badge,
BasicSeparator,
} from 'design-system';
import { sortBy } from 'lodash';
import { OpenAPIV3 } from 'openapi-types';
-import React, { Fragment, useMemo, useState } from 'react';
+import React, { Fragment, useContext, useMemo, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { translate } from '../../../helpers/l10n';
import { Dict } from '../../../types/types';
-import { URL_DIVIDER, getApiEndpointKey, getMethodClassName } from '../utils';
+import { InternalExtension } from '../types';
+import { URL_DIVIDER, getApiEndpointKey } from '../utils';
+import ApiFilterContext from './ApiFilterContext';
+import RestMethodPill from './RestMethodPill';
interface Api {
name: string;
method: string;
- info: OpenAPIV3.OperationObject<{ 'x-internal'?: 'true' }>;
+ info: OpenAPIV3.OperationObject<InternalExtension>;
}
interface Props {
docInfo: OpenAPIV3.InfoObject;
export default function ApiSidebar({ apisList, docInfo }: Readonly<Props>) {
const [search, setSearch] = useState('');
- const [showInternal, setShowInternal] = useState(false);
const navigate = useNavigate();
const location = useLocation();
+ const { showInternal, setShowInternal } = useContext(ApiFilterContext);
const activeApi = location.hash.replace('#', '').split(URL_DIVIDER);
const handleApiClick = (value: string) => {
<div className="sw-mt-4 sw-flex sw-items-center">
<Checkbox checked={showInternal} onCheck={() => setShowInternal((prev) => !prev)}>
- <span className="sw-ml-2">{translate('api_documentation.show_internal')}</span>
+ <span className="sw-ml-2">{translate('api_documentation.show_internal_v2')}</span>
</Checkbox>
- <HelpTooltip className="sw-ml-2" overlay={translate('api_documentation.internal_tooltip')}>
+ <HelpTooltip
+ className="sw-ml-2"
+ overlay={translate('api_documentation.internal_tooltip_v2')}
+ >
<HelperHintIcon aria-label="help-tooltip" />
</HelpTooltip>
</div>
onClick={handleApiClick}
value={getApiEndpointKey(name, method)}
>
- <div className="sw-flex sw-gap-2">
- <Badge className={classNames('sw-self-center', getMethodClassName(method))}>
- {method.toUpperCase()}
- </Badge>
- <div>{info.summary ?? name}</div>
+ <div className="sw-flex sw-gap-2 sw-w-full sw-justify-between">
+ <div className="sw-flex sw-gap-2">
+ <RestMethodPill method={method} />
+ <div>{info.summary ?? name}</div>
+ </div>
{(info['x-internal'] || info.deprecated) && (
<div className="sw-flex sw-flex-col sw-justify-center sw-gap-2">
--- /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 classNames from 'classnames';
+import { Badge } from 'design-system';
+import React from 'react';
+
+interface Props {
+ method: string;
+}
+
+export default function RestMethodPill({ method }: Readonly<Props>) {
+ const getMethodClassName = (): string => {
+ switch (method.toLowerCase()) {
+ case 'get':
+ return 'sw-bg-green-200';
+ case 'delete':
+ return 'sw-bg-red-200';
+ case 'post':
+ return 'sw-bg-blue-200';
+ case 'put':
+ return 'sw-bg-purple-200';
+ case 'patch':
+ return 'sw-bg-yellow-200';
+ default:
+ return 'sw-bg-gray-200';
+ }
+ };
+
+ return (
+ <Badge
+ className={classNames(
+ 'sw-self-center sw-align-middle sw-min-w-[50px] sw-text-center',
+ getMethodClassName(),
+ )}
+ >
+ {method.toUpperCase()}
+ </Badge>
+ );
+}
[K in keyof T]: DereferenceRecursive<T[K]>;
}
: T;
+
+export interface InternalExtension {
+ 'x-internal'?: 'true';
+}
return dereferenceRecursive(document) as ExcludeReferences<OpenAPIV3.Document>;
};
-export const getMethodClassName = (method: string): string => {
- switch (method.toLowerCase()) {
- case 'get':
- return 'sw-bg-green-200';
- case 'delete':
- return 'sw-bg-red-200';
- case 'post':
- return 'sw-bg-blue-200';
- case 'put':
- return 'sw-bg-purple-200';
- case 'patch':
- return 'sw-bg-yellow-200';
- default:
- return 'sw-bg-gray-200';
- }
-};
-
export const getResponseCodeClassName = (code: string): string => {
switch (code[0]) {
case '1':
#------------------------------------------------------------------------------
api_documentation.deprecation_tooltip=An API deprecated in version X.Y will be dropped in version (X+1).0. Example: an API deprecated in 4.1 is supported in 4.X (4.2, 4.3, etc.) and will be dropped in version 5.0.
api_documentation.internal_tooltip=Use at your own risk; internal services are subject to change or removal without notice.
+api_documentation.internal_tooltip_v2=Use at your own risk. Shows/hides the internal endpoints, parameters, and other details used for internal services. These are subject to change or removal without notice.
api_documentation.page=Web API
api_documentation.page.v2=Web API v2
api_documentation.show_deprecated=Show Deprecated API
api_documentation.show_internal=Show Internal API
+api_documentation.show_internal_v2=Show Internal
api_documentation.possible_values=Possible values
api_documentation.default_values=Default value
api_documentation.example_values=Example value