Skip to content

Commit

Permalink
WIP exploratory work
Browse files Browse the repository at this point in the history
  • Loading branch information
zchsh committed Oct 21, 2024
1 parent 2898133 commit d525776
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Types
import type { OpenAPIV3 } from 'openapi-types'
import type { OperationContentProps } from '.'
import { getOperationObjects } from 'views/open-api-docs-view-v2/utils/get-operation-objects'

/**
* TODO: transform the schemaData into useful props
Expand All @@ -14,10 +15,15 @@ export default async function getOperationContentProps(
operationSlug: string,
schemaData: OpenAPIV3.Document
): Promise<OperationContentProps> {
const operationObjects = getOperationObjects(schemaData)
const operationObject = operationObjects.find(
(operationObject) => operationObject.operationId === operationSlug
)
return {
_placeholder: {
viewToImplement: `Operation view for ${operationSlug}`,
schemaSample: schemaData.info,
operationObject,
},
}
}
74 changes: 74 additions & 0 deletions src/views/open-api-docs-view-v2/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

// Components
import SidebarBackToLink from '@components/sidebar/components/sidebar-back-to-link'
import { SidebarNavMenuItem } from '@components/sidebar/components'
// Types
import type { OpenApiNavItem } from 'views/open-api-docs-view-v2/types'
// Styles
import s from './style.module.css'

/**
* TODO: lift this content up so it can vary page-to-page
*/
const SHIM_CONTENT = {
backToLink: {
text: 'HashiCorp Cloud Platform',
href: '/hcp',
},
}

export function OpenApiV2SidebarContents({ navItemLanding, navItemGroups }) {
/**
* TODO: refine generation of nav items, and then render them properly,
* for now just messily rendering some links to enable navigation.
*
* Note: `next/link` will work in prod, since we'll be doing
* `getStaticProps`... but in the preview tool, `next/link` seems to
* make the preview experience janky, seemingly requiring reloads after
* each navigation, maybe related to use of getServerSideProps? Not yet
* sure how to resolve this, there's probably some clever solution that
* might be possible...
*/
return (
<>
<SidebarBackToLink
text={SHIM_CONTENT.backToLink.text}
href={SHIM_CONTENT.backToLink.text}
/>
<ul className={s.listResetStyles}>
<SidebarNavMenuItem item={navItemLanding} />
{navItemGroups.map((navItemGroup) => {
return (
<ul className={s.listResetStyles}>
<SidebarNavMenuItem item={{ heading: navItemGroup.title }} />
<li>
<SidebarNavMenuItemsList items={navItemGroup.items} />
</li>
</ul>
)
})}
</ul>
</>
)
}
/**
* Renders an unordered list of nav items, with list styles reset.
*/
export function SidebarNavMenuItemsList({
items,
}: {
items: OpenApiNavItem[]
}) {
return (
<ul className={s.listResetStyles}>
{items.map((item: OpenApiNavItem, index: number) => (
// eslint-disable-next-line react/no-array-index-key
<SidebarNavMenuItem item={item} key={index} />
))}
</ul>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.listResetStyles {
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
46 changes: 17 additions & 29 deletions src/views/open-api-docs-view-v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SidebarLayout from 'layouts/sidebar-layout'
// Components
import LandingContent from './components/landing-content'
import OperationContent from './components/operation-content'
import { OpenApiV2SidebarContents } from './components/sidebar'
// Types
import type { OpenApiDocsViewV2Props } from './types'

Expand All @@ -19,40 +20,18 @@ import type { OpenApiDocsViewV2Props } from './types'
*/
export default function OpenApiDocsViewV2({
basePath,
navItems,
navItemLanding,
navItemGroups,
...restProps
}: OpenApiDocsViewV2Props) {
//
return (
<SidebarLayout
sidebarSlot={
/**
* TODO: refine generation of nav items, and then render them properly,
* for now just messily rendering some links to enable navigation.
*
* Note: `next/link` will work in prod, since we'll be doing
* `getStaticProps`... but in the preview tool, `next/link` seems to
* make the preview experience janky, seemingly requiring reloads after
* each navigation, maybe related to use of getServerSideProps? Not yet
* sure how to resolve this, there's probably some clever solution that
* might be possible...
*/
<ul style={{ margin: 0, border: '1px solid magenta' }}>
{navItems.map((navItem) => {
if (!('fullPath' in navItem)) {
return null
}
return (
<li key={navItem.fullPath}>
<a
href={navItem.fullPath}
style={{ color: navItem.isActive ? 'white' : undefined }}
>
{navItem.title}
</a>
</li>
)
})}
</ul>
<OpenApiV2SidebarContents
navItemLanding={navItemLanding}
navItemGroups={navItemGroups}
/>
}
/**
* TODO: implement mobile menu. May be tempting to try to re-use the data
Expand All @@ -64,6 +43,15 @@ export default function OpenApiDocsViewV2({
mobileMenuSlot={null}
>
<div style={{ padding: '24px' }}>
<div style={{ border: '1px solid magenta' }}>
{'_sidebarPlaceholder' in restProps ? (
<pre style={{ whiteSpace: 'pre-wrap' }}>
<code>
{JSON.stringify(restProps._sidebarPlaceholder, null, 2)}
</code>
</pre>
) : null}
</div>
<div style={{ border: '1px solid magenta' }}>
{'operationContentProps' in restProps ? (
<OperationContent {...restProps.operationContentProps} />
Expand Down
106 changes: 104 additions & 2 deletions src/views/open-api-docs-view-v2/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,90 @@ import { parseAndValidateOpenApiSchema } from 'lib/api-docs/parse-and-validate-o
// Utils
import getOperationContentProps from './components/operation-content/server'
import getLandingContentProps from './components/landing-content/server'
import { getOperationObjects } from './utils/get-operation-objects'
import { buildOperationGroups } from './utils/build-operation-groups'
import { wordBreakCamelCase } from './utils/word-break-camel-case'
// Types
import type {
OpenApiDocsViewV2Props,
OpenApiNavItem,
SharedProps,
} from 'views/open-api-docs-view-v2/types'
// import { stripUndefinedProperties } from 'lib/strip-undefined-props'

/**
* TODO: lift this content up so it can vary page-to-page
*
* THOUGHT: rather than require custom YAML, maybe we should use a second layer
* of "tags" to group operations further? Maybe the custom YAML could be used
* at the "spec hook" stage... to add that second layer of tags. And then our
* operation grouping logic could be based on tags. This would open the door
* to have these changes made at the content source in the near future (at
* which point we'd remove the "spec hook" stage).
*
* Reference doc:
* https://swagger.io/docs/specification/v3_0/grouping-operations-with-tags/
*/
const SHIM_CONTENT = {
operationGroupings: [
{
title: 'Apps',
operationIds: [
'CreateApp',
'ListApps',
'GetApp',
'UpdateApp',
'DeleteApp',
],
},
{
title: 'Secrets',
operationIds: [
'CreateAppKVSecret',
'OpenAppSecrets',
'OpenAppSecret',
'ListAppSecrets',
'GetAppSecret',
'DeleteAppSecret',
],
},
{
title: 'Secret Versions',
operationIds: [
'ListAppSecretVersions',
'ListOpenAppSecretVersions',
'OpenAppSecretVersion',
'GetAppSecretVersion',
'DeleteAppSecretVersion',
],
},
{
title: 'Sync Integrations',
operationIds: [
'CreateAwsSmSyncIntegration',
'CreateAzureKvSyncIntegration',
],
},
{
title: 'Sync Integrations',
operationIds: [
'CreateGcpSmSyncIntegration',
'CreateGhOrgSyncIntegration',
'CreateGhRepoSyncIntegration',
],
},
{
title: 'GitHub Installations',
operationIds: [
'ListGitHubInstallations',
'ConnectGitHubInstallation',
'GetGitHubInstallLinks',
],
},
// ForceSync
// SetTier
],
}

export async function getStaticProps({
basePath,
Expand Down Expand Up @@ -47,8 +126,31 @@ export async function getStaticProps({
* complex version may end up meaning significant differences in the logic
* to generate the version selector depending on landing vs operation view.
*/
const navItems = getNavItems(basePath, operationSlug, schemaData)
const sharedProps: SharedProps = { basePath, navItems }
const operationObjects = getOperationObjects(schemaData)
const operationGroups = buildOperationGroups(
SHIM_CONTENT.operationGroupings,
operationObjects
)
const navItemLanding: OpenApiNavItem = {
title: 'Landing',
fullPath: basePath,
isActive: !operationSlug,
}
const navItemGroups = operationGroups.map((group) => ({
title: group.title,
items: group.operationObjects.map(({ type, operationId }) => {
return {
title: wordBreakCamelCase(operationId),
fullPath: `${basePath}/${operationId}`,
isActive: operationSlug === operationId,
}
}),
}))
const sharedProps: SharedProps = {
basePath,
navItemLanding,
navItemGroups,
}

/**
* If we have an operation slug, build and return operation view props.
Expand Down
3 changes: 2 additions & 1 deletion src/views/open-api-docs-view-v2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export type OpenApiNavItem = {
*/
export interface SharedProps {
basePath: string
navItems: OpenApiNavItem[]
navItemLanding: OpenApiNavItem
navItemGroups: { title: string; items: OpenApiNavItem[] }[]
}

/**
Expand Down
71 changes: 71 additions & 0 deletions src/views/open-api-docs-view-v2/utils/build-operation-groups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

interface OperationGroup {
title: string
operationObjects: $TSFixMe
}

/**
* TODO: implement properly and add comments,
* messing around for now.
*/
export function buildOperationGroups(
operationGroupings: $TSFixMe,
operationObjects: $TSFixMe
): OperationGroup[] {
const operationGroups: OperationGroup[] = []
//
for (const { title, operationIds } of operationGroupings) {
/**
* Match each operationId to an operation object
* If given operationId doesn't have a match... error? warn?
*/
const matchedOperationObjects = []
for (const operationId of operationIds) {
const matchedOperationObject = operationObjects.find(
(operationObject) => operationObject.operationId === operationId
)
console.log({ operationId, matchedOperationObject })
if (matchedOperationObject) {
matchedOperationObjects.push(matchedOperationObject)
} else {
console.error(
`No operation object found for operationId: ${operationId}. Skipping.`
)
}
}
//
if (matchedOperationObjects.length === 0) {
console.error(
`No operation objects found for group: ${title}. Skipping group.`
)
} else {
operationGroups.push({ title, operationObjects: matchedOperationObjects })
}
}
// Gather ids from all operation groups
const allUsedOperationIds = operationGroups.reduce(
(acc, group) =>
acc.concat(group.operationObjects.map((obj) => obj.operationId)),
[]
)
// Check for any operation objects that weren't used
const unusedOperationObjects = operationObjects.filter(
(obj) => !allUsedOperationIds.includes(obj.operationId)
)
/**
* If we have operation objects that were not included in a group,
* create an "Other" group to hold any unused operation objects.
*/
if (unusedOperationObjects.length > 0) {
operationGroups.push({
title: 'Other', // TODO: make this configurable? Arg with default value?
operationObjects: unusedOperationObjects,
})
}
//
return operationGroups
}
Loading

0 comments on commit d525776

Please sign in to comment.