Skip to content

Commit

Permalink
implement versioned docs for OpenAPI w split operation pages (#2623)
Browse files Browse the repository at this point in the history
* copy over utilities from previous view

* clean up parse-and-validate-schema

* implement versioning for OpenApiDocsViewV2

* update productData to product, for nav context

* fix issue with productData

* rejig default theme

* fix metadata for landing
  • Loading branch information
zchsh authored Nov 12, 2024
1 parent 04d9ef0 commit 672d571
Show file tree
Hide file tree
Showing 20 changed files with 786 additions and 223 deletions.
15 changes: 10 additions & 5 deletions src/lib/api-docs/parse-and-validate-open-api-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ import { OpenAPIV3 } from 'openapi-types'
*/
export async function parseAndValidateOpenApiSchema(
fileString: string,
massageSchemaForClient: (schema: OpenAPIV3.Document) => OpenAPIV3.Document = (
schema
) => schema
schemaTransforms: ((schema: OpenAPIV3.Document) => OpenAPIV3.Document)[] = []
): Promise<OpenAPIV3.Document> {
// Parse the fileString into raw JSON
const rawSchemaJson = JSON.parse(fileString)
Expand All @@ -41,7 +39,14 @@ export async function parseAndValidateOpenApiSchema(
const schemaJsonWithRefs = await new OASNormalize(rawSchemaJson).validate({
convertToLatest: true,
})
const massagedSchema = massageSchemaForClient(schemaJsonWithRefs)

/**
* Apply transform functions to the schema
*/
let transformedSchema = schemaJsonWithRefs
for (const schemaTransformFunction of schemaTransforms ?? []) {
transformedSchema = schemaTransformFunction(transformedSchema)
}

/**
* Dereference the schema.
Expand Down Expand Up @@ -69,7 +74,7 @@ export async function parseAndValidateOpenApiSchema(
* }
* }
*/
const schemaJson = await new OASNormalize(massagedSchema).deref()
const schemaJson = await new OASNormalize(transformedSchema).deref()
// Return the dereferenced schema.
// We know it's OpenAPI 3.0, so we cast it to the appropriate type.
return schemaJson as OpenAPIV3.Document
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: MPL-2.0
*/

import { getStaticProps } from 'views/open-api-docs-view-v2/server'
import { generateStaticProps } from 'views/open-api-docs-view-v2/server'
// Utils
import { getOperationGroupKeyFromPath } from 'views/open-api-docs-view-v2/utils/get-operation-group-key-from-path'
import { schemaTransformShortenHcp } from 'views/open-api-docs-view-v2/schema-transforms/schema-transform-shorten-hcp'
Expand All @@ -15,19 +15,13 @@ import type {
OpenApiDocsViewV2Config,
} from 'views/open-api-docs-view-v2/types'
import type { OpenApiPreviewV2InputValues } from '../components/open-api-preview-inputs'
import { schemaModComponent } from 'views/open-api-docs-view/utils/massage-schema-utils'

/**
* Given preview data submitted by the user, which includes OpenAPI JSON,
* and given an optional operation slug that indicates whether to render
* a view for a specific operation,
*
* Return static props for the appropriate OpenAPI docs view.
*
* TODO: this is largely a placeholder for now.
* Will likely require a few more args to pass to getStaticProps, eg productData
* for example, but those types of details are not yet needed by the underlying
* view.
*/
export default async function getPropsFromPreviewData(
previewData: OpenApiPreviewV2InputValues | null,
Expand Down Expand Up @@ -59,17 +53,16 @@ export default async function getPropsFromPreviewData(
},
]
// Build page configuration based on the input values
const pageConfig: OpenApiDocsViewV2Config = {
const pageConfig: Omit<OpenApiDocsViewV2Config, 'schemaSource'> = {
basePath: '/open-api-docs-preview-v2',
breadcrumbLinksPrefix: [
{
title: 'Developer',
url: '/',
},
],
operationSlug,
openApiJsonString: previewData.openApiJsonString,
schemaTransforms,
productContext: 'hcp',
// A generic set of resource links, as a preview of what typically
// gets added to an OpenAPI docs page.
resourceLinks: [
Expand All @@ -90,8 +83,6 @@ export default async function getPropsFromPreviewData(
href: 'https://www.hashicorp.com/customer-success',
},
],
// Release stage badge, to demo this feature
releaseStage: 'Preview',
// Status indicator for HCP Services generally, to demo this feature
statusIndicatorConfig: {
pageUrl: 'https://status.hashicorp.com',
Expand All @@ -106,5 +97,25 @@ export default async function getPropsFromPreviewData(
pageConfig.getOperationGroupKey = getOperationGroupKeyFromPath
}
// Use the page config to generate static props for the view
return await getStaticProps(pageConfig)
const staticProps = await generateStaticProps({
...pageConfig,
versionData: [
{
versionId: 'latest',
releaseStage: 'Preview',
sourceFile: previewData.openApiJsonString,
},
],
urlContext: {
isVersionedUrl: false,
versionId: 'latest',
operationSlug,
},
})
// If the specific view wasn't found, return null
if ('notFound' in staticProps) {
return null
}
// Otherwise, return the props, discarding the enclosing object
return staticProps.props
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@ import { DescriptionMdx } from './components/description-mdx'
// Types
import type { MDXRemoteSerializeResult } from 'lib/next-mdx-remote'
import type { StatusIndicatorConfig } from 'views/open-api-docs-view-v2/types'

import type { ReactNode } from 'react'
import type { ProductSlug } from 'types/products'
// Styles
import s from './style.module.css'

export interface LandingContentProps {
badgeText: string
descriptionMdx?: MDXRemoteSerializeResult
heading: string
serviceProductSlug: ProductSlug
schemaFileString?: string
badgeText?: string
descriptionMdx?: MDXRemoteSerializeResult
serviceProductSlug?: ProductSlug
statusIndicatorConfig?: StatusIndicatorConfig
schemaFileString: string
versionSwitcherSlot?: ReactNode
}

export function LandingContent({
Expand All @@ -36,6 +37,7 @@ export function LandingContent({
serviceProductSlug,
statusIndicatorConfig,
schemaFileString,
versionSwitcherSlot,
}: LandingContentProps) {
return (
<div className={s.overviewWrapper}>
Expand All @@ -53,26 +55,33 @@ export function LandingContent({
/>
) : null}
</span>
<Badge
className={s.releaseStageBadge}
text={badgeText}
type="outlined"
size="small"
/>
{badgeText ? (
<Badge
className={s.releaseStageBadge}
text={badgeText}
type="outlined"
size="small"
/>
) : null}
</header>
{versionSwitcherSlot ? (
<div className={s.versionSwitcherSlot}>{versionSwitcherSlot}</div>
) : null}
</div>
{descriptionMdx ? (
<DescriptionMdx mdxRemoteProps={descriptionMdx} />
) : null}
<StandaloneLink
text="Download Spec"
icon={<IconDownload16 />}
iconPosition="leading"
download="hcp.swagger.json"
href={`data:text/json;charset=utf-8,${encodeURIComponent(
schemaFileString
)}`}
/>
{schemaFileString ? (
<StandaloneLink
text="Download Spec"
icon={<IconDownload16 />}
iconPosition="leading"
download="hcp.swagger.json"
href={`data:text/json;charset=utf-8,${encodeURIComponent(
schemaFileString
)}`}
/>
) : null}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import { OperationExamples } from '../operation-examples'
import { OperationDetails } from '../operation-details'
// Types
import type { PropertyDetailsSectionProps } from '../operation-details'
import type { ReactNode } from 'react'
// Styles
import s from './style.module.css'

export interface OperationContentProps {
heading: string
operationId: string
tags: string[]
slug: string
Expand All @@ -32,6 +32,7 @@ export interface OperationContentProps {
* word breaks to allow long URLs to wrap to multiple lines.
*/
urlPathForCodeBlock: string
versionSwitcherSlot?: ReactNode
}

/**
Expand All @@ -47,17 +48,22 @@ export interface OperationProps {
* Render detailed content for an individual operation.
*/
export default function OperationContent({
heading,
slug,
operationId,
type,
path,
urlPathForCodeBlock,
requestData,
responseData,
versionSwitcherSlot,
}: OperationContentProps) {
return (
<>
<h1 className={s.heading}>{heading}</h1>
<div className={s.header}>
<h1 className={s.heading}>{operationId}</h1>
{versionSwitcherSlot ? (
<div className={s.versionSwitcherSlot}>{versionSwitcherSlot}</div>
) : null}
</div>
<OperationSections
headerSlot={
<div className={s.methodAndPath}>
Expand All @@ -66,7 +72,7 @@ export default function OperationContent({
</div>
}
examplesSlot={
<OperationExamples heading={slug} code={urlPathForCodeBlock} />
<OperationExamples heading={operationId} code={urlPathForCodeBlock} />
}
detailsSlot={
<OperationDetails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,30 @@
*/

// Utils
import { getOperationObjects } from '../../utils/get-operation-objects'

import { getUrlPathCodeHtml } from '../../utils/get-url-path-code-html'
import { truncateHcpOperationPath } from '../../utils/truncate-hcp-operation-path'
import { getRequestData } from '../../utils/get-request-data'
import { getResponseData } from '../../utils/get-response-data'
import { slugifyOperationId } from 'views/open-api-docs-view-v2/utils/slugify-operation-id'
// Types
import type { OpenAPIV3 } from 'openapi-types'
import type { OperationContentProps } from '.'
import type { OperationObject } from '../../utils/get-operation-objects'

/**
* Transform the schemaData into props for an individual operation
*/
export default async function getOperationContentProps(
operationSlug: string,
operation: OperationObject,
schemaData: OpenAPIV3.Document
): Promise<OperationContentProps> {
const operationObjects = getOperationObjects(schemaData)
const operation = operationObjects.find(
(operation) => operation.operationId === operationSlug
)
/**
* The API's base URL is used to prefix the operation path,
* so users can quickly copy the full path to the operation
*/
const apiBaseUrl = getApiBaseUrl(schemaData)
const operationSlug = slugifyOperationId(operation.operationId)
/**
* Parse request and response details for this operation
*/
Expand All @@ -53,8 +52,7 @@ export default async function getOperationContentProps(
* Return the operation content props
*/
return {
heading: operationSlug,
operationId: operationSlug,
operationId: operation.operationId,
tags: operation.tags,
slug: operationSlug,
type: operation.type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
* SPDX-License-Identifier: MPL-2.0
*/

/* HEADER AREA */

.header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 16px;
}

.heading {
composes: hds-typography-display-600 from global;

Expand All @@ -13,7 +22,9 @@
margin: 0 0 9px 0;
}

/* HEADER AREA */
.versionSwitcherSlot {
flex-shrink: 0;
}

.methodAndPath {
display: flex;
Expand Down
Loading

0 comments on commit 672d571

Please sign in to comment.