-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DEV-1040] Add OpenAPI component (#353)
- Loading branch information
1 parent
bcbe0b4
commit b2f50b8
Showing
25 changed files
with
2,149 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"nextjs-website": minor | ||
"gitbook-docs": patch | ||
--- | ||
|
||
[DEV-1040] Rendering component OpenAPI Gitbook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 0 additions & 5 deletions
5
apps/nextjs-website/src/components/organisms/GitBookContent/components/Swagger.tsx
This file was deleted.
Oops, something went wrong.
148 changes: 148 additions & 0 deletions
148
apps/nextjs-website/src/components/organisms/GitBookContent/components/Swagger/Model.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { KeyboardArrowRight } from '@mui/icons-material'; | ||
import { | ||
Box, | ||
Collapse, | ||
List, | ||
ListItemButton, | ||
ListItemIcon, | ||
Typography, | ||
} from '@mui/material'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
import { MouseEventHandler, PropsWithChildren, useState } from 'react'; | ||
import { useModelProps } from './hooks/useModel'; | ||
|
||
type ModelEntryProps = { | ||
title?: string; | ||
required?: boolean; | ||
schemaType?: string; | ||
}; | ||
|
||
type ModelItemProps = ModelEntryProps & { | ||
description?: string; | ||
onClick?: MouseEventHandler<HTMLDivElement>; | ||
}; | ||
|
||
const ModelItem = ({ | ||
description, | ||
title, | ||
required, | ||
schemaType, | ||
onClick, | ||
}: ModelItemProps) => { | ||
const showIcon = typeof onClick === 'function'; | ||
return ( | ||
<ListItemButton | ||
sx={{ | ||
display: 'block', | ||
background: 'transparent!important', | ||
py: 1, | ||
}} | ||
disableGutters | ||
onClick={onClick} | ||
> | ||
<Box | ||
sx={{ | ||
display: 'inline-flex', | ||
alignItems: 'center', | ||
flexGrow: 1, | ||
width: '100%', | ||
gap: 2, | ||
}} | ||
> | ||
<ListItemIcon> | ||
{showIcon && <KeyboardArrowRight sx={{ fontSize: '1.125rem' }} />} | ||
</ListItemIcon> | ||
{title && ( | ||
<Typography sx={{ fontWeight: 'bold' }}> | ||
{title} | ||
{required && ( | ||
<Typography component='span' color='red'> | ||
* | ||
</Typography> | ||
)} | ||
</Typography> | ||
)} | ||
<Typography sx={{ color: (theme) => theme.palette.primary.main }}> | ||
{schemaType} | ||
</Typography> | ||
</Box> | ||
<Typography sx={{ ml: 2 }}>{description}</Typography> | ||
</ListItemButton> | ||
); | ||
}; | ||
|
||
const ModelListEntry = ({ | ||
title, | ||
required, | ||
schemaType, | ||
children, | ||
}: PropsWithChildren<ModelEntryProps>) => { | ||
const [open, setOpen] = useState(false); | ||
|
||
return ( | ||
<List disablePadding> | ||
<ModelItem | ||
title={title} | ||
required={required} | ||
schemaType={schemaType} | ||
onClick={() => setOpen(!open)} | ||
/> | ||
<Collapse | ||
sx={{ borderLeft: 1, borderColor: 'divider', ml: 1, pl: 2 }} | ||
in={open} | ||
timeout='auto' | ||
unmountOnExit | ||
> | ||
{children} | ||
</Collapse> | ||
</List> | ||
); | ||
}; | ||
|
||
type ModelProps = { | ||
label?: string; | ||
model: OpenAPIV3.SchemaObject; | ||
requiredAttrs?: ReadonlyArray<string>; | ||
}; | ||
|
||
export const Model = (props: ModelProps) => { | ||
const { | ||
description, | ||
items, | ||
properties, | ||
required, | ||
requiredAttrs, | ||
schemaType, | ||
title, | ||
} = useModelProps(props); | ||
|
||
if (schemaType === 'object') { | ||
return ( | ||
<ModelListEntry title={title} required={required} schemaType={schemaType}> | ||
{Object.entries(properties).map(([key, property]) => ( | ||
<Model | ||
key={key} | ||
label={key} | ||
model={property} | ||
requiredAttrs={requiredAttrs} | ||
/> | ||
))} | ||
</ModelListEntry> | ||
); | ||
} else if (schemaType === 'array') { | ||
return ( | ||
<ModelListEntry title={title} required={required} schemaType={schemaType}> | ||
<Model model={items} requiredAttrs={requiredAttrs} /> | ||
</ModelListEntry> | ||
); | ||
} else { | ||
return ( | ||
<ModelItem | ||
title={title} | ||
required={required} | ||
schemaType={schemaType} | ||
description={description} | ||
/> | ||
); | ||
} | ||
}; |
91 changes: 91 additions & 0 deletions
91
apps/nextjs-website/src/components/organisms/GitBookContent/components/Swagger/Operation.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { | ||
Box, | ||
Chip, | ||
ChipProps, | ||
Typography, | ||
chipClasses, | ||
styled, | ||
} from '@mui/material'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
|
||
import Expandable, { | ||
ExpandableDetails, | ||
ExpandableSummary, | ||
} from '../Expandable'; | ||
import { Parameters } from './Parameters'; | ||
import { RequestBody } from './RequestBody'; | ||
import { Responses } from './Responses'; | ||
|
||
const StyledChip = styled(Chip)(() => ({ | ||
[`& .${chipClasses.label}`]: { | ||
textTransform: 'uppercase', | ||
fontWeight: 'bold', | ||
color: 'white!important', | ||
}, | ||
})); | ||
|
||
export const API_METHODS_COLORS: Record< | ||
OpenAPIV3.HttpMethods, | ||
ChipProps['color'] | ||
> = { | ||
get: 'primary', | ||
post: 'success', | ||
put: 'warning', | ||
delete: 'error', | ||
options: 'default', | ||
head: 'default', | ||
patch: 'default', | ||
trace: 'default', | ||
}; | ||
|
||
type OperationProps = OpenAPIV3.OperationObject<{ | ||
method: OpenAPIV3.HttpMethods; | ||
path: string; | ||
}>; | ||
|
||
export const Operation = ({ | ||
method, | ||
path, | ||
summary, | ||
description, | ||
parameters, | ||
responses, | ||
requestBody, | ||
servers = [], | ||
}: OperationProps) => { | ||
const chipColor = API_METHODS_COLORS[method] || 'default'; | ||
const baseUrl = servers[0]?.url || ''; | ||
|
||
return ( | ||
<Expandable> | ||
<ExpandableSummary> | ||
<Box display='inline-flex' alignItems='center' flexWrap='wrap'> | ||
<StyledChip | ||
sx={{ mr: 2 }} | ||
label={method} | ||
color={chipColor} | ||
size='small' | ||
/> | ||
<Typography | ||
variant='caption' | ||
sx={{ color: (theme) => theme.palette.text.secondary }} | ||
> | ||
{baseUrl} | ||
</Typography> | ||
<Typography variant='caption-semibold'>{path}</Typography> | ||
</Box> | ||
<Typography sx={{ fontWeight: 'bold', mt: 2 }} variant='body1'> | ||
{summary} | ||
</Typography> | ||
</ExpandableSummary> | ||
<ExpandableDetails> | ||
<Typography variant='body2'>{description}</Typography> | ||
<Parameters parameters={parameters as OpenAPIV3.ParameterObject[]} /> | ||
{requestBody && ( | ||
<RequestBody {...(requestBody as OpenAPIV3.RequestBodyObject)} /> | ||
)} | ||
{responses && <Responses responses={responses} />} | ||
</ExpandableDetails> | ||
</Expandable> | ||
); | ||
}; |
93 changes: 93 additions & 0 deletions
93
.../nextjs-website/src/components/organisms/GitBookContent/components/Swagger/Operations.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { useTranslations } from 'next-intl'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
|
||
import { Operation } from './Operation'; | ||
import { Card, Typography } from '@mui/material'; | ||
|
||
const methods = [ | ||
'get', | ||
'post', | ||
'put', | ||
'delete', | ||
'options', | ||
'head', | ||
'patch', | ||
'trace', | ||
]; | ||
|
||
const getOperations = ( | ||
pathItemObj: Omit<OpenAPIV3.PathItemObject, 'parameters'> | ||
) => | ||
Object.keys(pathItemObj) | ||
.filter((key) => methods.includes(key)) | ||
.reduce<[string, OpenAPIV3.OperationObject][]>((acc, key) => { | ||
const method = key as OpenAPIV3.HttpMethods; | ||
const operation = pathItemObj[method] as OpenAPIV3.OperationObject; | ||
return [...acc, [method, operation]]; | ||
}, []); | ||
|
||
type OperationsProps = { | ||
spec: OpenAPIV3.Document; | ||
validOperations?: Record<string, OpenAPIV3.HttpMethods[]>; | ||
}; | ||
|
||
export const Operations = ({ spec, validOperations }: OperationsProps) => { | ||
const t = useTranslations('swagger'); | ||
|
||
if (!spec.paths || !validOperations) { | ||
const noOpHeader = t('emptyOperations.header'); | ||
const noOpMessage = t('emptyOperations.message'); | ||
return ( | ||
<Card sx={{ borderRadius: 1, p: 2 }} variant='outlined'> | ||
<Typography sx={{ fontWeight: 'bold' }} variant='body1'> | ||
{noOpHeader} | ||
</Typography> | ||
<Typography | ||
variant='body2' | ||
sx={{ color: (theme) => theme.palette.text.secondary }} | ||
> | ||
{noOpMessage} | ||
</Typography> | ||
</Card> | ||
); | ||
} | ||
|
||
const paths = Object.entries(spec.paths); | ||
const specServers = spec.servers || []; | ||
|
||
const renderOperationTag = ([path, pathItemObj = {}]: [ | ||
string, | ||
OpenAPIV3.PathItemObject | undefined | ||
]) => { | ||
const operations = getOperations(pathItemObj); | ||
const headerParameters = pathItemObj?.parameters || []; | ||
const validOperationMethods = validOperations[path] || []; | ||
|
||
return ( | ||
<div key={`operation-${path}`}> | ||
{operations.map(([method, operation]) => { | ||
const httpMethod = method as OpenAPIV3.HttpMethods; | ||
const renderOp = validOperationMethods.includes(httpMethod); | ||
|
||
if (!renderOp) return null; | ||
|
||
const { parameters: pathParameters = [], servers, ...op } = operation; | ||
const parameters = pathParameters.concat(headerParameters); | ||
const operationServers = servers || specServers; | ||
return ( | ||
<Operation | ||
key={`${path}-${method}`} | ||
method={httpMethod} | ||
path={path} | ||
parameters={parameters} | ||
servers={operationServers} | ||
{...op} | ||
/> | ||
); | ||
})} | ||
</div> | ||
); | ||
}; | ||
|
||
return <div>{paths.map(renderOperationTag)}</div>; | ||
}; |
Oops, something went wrong.