mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Use OpenAPI publicURL as Scalar specUrl (#4093)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Use OpenAPI publicURL as Scalar specUrl
|
||||
@@ -26,7 +26,7 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data, specUrl, error } = await resolveOpenAPIOperationBlock({
|
||||
const { data, specUrl, publicURL, error } = await resolveOpenAPIOperationBlock({
|
||||
block,
|
||||
context: context.contentContext,
|
||||
});
|
||||
@@ -46,7 +46,11 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
|
||||
return (
|
||||
<BaseOpenAPIOperation
|
||||
data={data}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
context={getOpenAPIContext({
|
||||
props,
|
||||
specUrl: publicURL,
|
||||
context: context.contentContext,
|
||||
})}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data, specUrl, error } = await resolveOpenAPISchemasBlock({
|
||||
const { data, specUrl, publicURL, error } = await resolveOpenAPISchemasBlock({
|
||||
block,
|
||||
context: context.contentContext,
|
||||
});
|
||||
@@ -48,7 +48,11 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
|
||||
<BaseOpenAPISchemas
|
||||
schemas={data.schemas}
|
||||
grouped={block.data.grouped}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
context={getOpenAPIContext({
|
||||
props,
|
||||
specUrl: publicURL,
|
||||
context: context.contentContext,
|
||||
})}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data, specUrl, error } = await resolveOpenAPIWebhookBlock({
|
||||
const { data, specUrl, publicURL, error } = await resolveOpenAPIWebhookBlock({
|
||||
block,
|
||||
context: context.contentContext,
|
||||
});
|
||||
@@ -48,7 +48,11 @@ async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
|
||||
return (
|
||||
<BaseOpenAPIWebhook
|
||||
data={data}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
context={getOpenAPIContext({
|
||||
props,
|
||||
specUrl: publicURL,
|
||||
context: context.contentContext,
|
||||
})}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ import type {
|
||||
*/
|
||||
export function getOpenAPIContext(args: {
|
||||
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
|
||||
specUrl: string;
|
||||
specUrl: string | null;
|
||||
context: GitBookAnyContext | undefined;
|
||||
}): OpenAPIContextInput {
|
||||
const { props, specUrl, context } = args;
|
||||
|
||||
@@ -33,14 +33,14 @@ export async function fetchOpenAPIFilesystem(
|
||||
const resolved = ref ? await resolveContentRef(ref, context) : null;
|
||||
|
||||
if (!resolved) {
|
||||
return { filesystem: null, specUrl: null };
|
||||
return { filesystem: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
const result = await (() => {
|
||||
// If the reference is a new OpenAPI reference, we return it.
|
||||
if (ref.kind === 'openapi') {
|
||||
assert(resolved.openAPIFilesystem);
|
||||
return resolved.openAPIFilesystem;
|
||||
assert(resolved.openapi?.filesystem);
|
||||
return resolved.openapi.filesystem;
|
||||
}
|
||||
// For legacy blocks ("swagger"), we need to fetch the file system.
|
||||
return fetchFilesystem(resolved.href, context.space.id);
|
||||
@@ -50,7 +50,21 @@ export async function fetchOpenAPIFilesystem(
|
||||
throw new OpenAPIParseError(result.error.message, { code: result.error.code });
|
||||
}
|
||||
|
||||
return { filesystem: result, specUrl: resolved.href };
|
||||
const publicURL = (() => {
|
||||
// For new OpenAPI refs, use the explicit publicURL (null when spec is private).
|
||||
if (ref.kind === 'openapi' && resolved.openapi) {
|
||||
return resolved.openapi.publicURL;
|
||||
}
|
||||
|
||||
// For legacy "swagger" refs, the href is the public spec URL itself.
|
||||
return resolved.href;
|
||||
})();
|
||||
|
||||
return {
|
||||
filesystem: result,
|
||||
specUrl: resolved.href,
|
||||
publicURL,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,14 +37,14 @@ async function resolveOpenAPIOperationBlockNoCache(
|
||||
const { context, block } = args;
|
||||
|
||||
if (!block.data.path || !block.data.method) {
|
||||
return { data: null, specUrl: null };
|
||||
return { data: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
try {
|
||||
const { filesystem, specUrl } = await fetchOpenAPIFilesystem({ block, context });
|
||||
const { filesystem, specUrl, publicURL } = await fetchOpenAPIFilesystem({ block, context });
|
||||
|
||||
if (!filesystem) {
|
||||
return { data: null, specUrl: null };
|
||||
return { data: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
const data = await resolveOpenAPIOperation(filesystem, {
|
||||
@@ -52,7 +52,7 @@ async function resolveOpenAPIOperationBlockNoCache(
|
||||
method: block.data.method,
|
||||
});
|
||||
|
||||
return { data, specUrl };
|
||||
return { data, specUrl, publicURL };
|
||||
} catch (error) {
|
||||
if (error instanceof OpenAPIParseError) {
|
||||
return { error };
|
||||
|
||||
@@ -37,21 +37,21 @@ async function baseResolveOpenAPISchemasBlock(
|
||||
): Promise<ResolveOpenAPISchemasBlockResult> {
|
||||
const { context, block } = args;
|
||||
if (!block.data.schemas || !block.data.schemas.length) {
|
||||
return { data: null, specUrl: null };
|
||||
return { data: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
try {
|
||||
const { filesystem, specUrl } = await fetchOpenAPIFilesystem({ block, context });
|
||||
const { filesystem, specUrl, publicURL } = await fetchOpenAPIFilesystem({ block, context });
|
||||
|
||||
if (!filesystem || !specUrl) {
|
||||
return { data: null, specUrl: null };
|
||||
return { data: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
const data = await resolveOpenAPISchemas(filesystem, {
|
||||
schemas: block.data.schemas,
|
||||
});
|
||||
|
||||
return { data, specUrl };
|
||||
return { data, specUrl, publicURL };
|
||||
} catch (error) {
|
||||
if (error instanceof OpenAPIParseError) {
|
||||
return { error };
|
||||
|
||||
@@ -35,14 +35,14 @@ async function baseResolveOpenAPIWebhookBlock(
|
||||
): Promise<ResolveOpenAPIWebhookBlockResult> {
|
||||
const { context, block } = args;
|
||||
if (!block.data.name || !block.data.method) {
|
||||
return { data: null, specUrl: null };
|
||||
return { data: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
try {
|
||||
const { filesystem, specUrl } = await fetchOpenAPIFilesystem({ block, context });
|
||||
const { filesystem, specUrl, publicURL } = await fetchOpenAPIFilesystem({ block, context });
|
||||
|
||||
if (!filesystem) {
|
||||
return { data: null, specUrl: null };
|
||||
return { data: null, specUrl: null, publicURL: null };
|
||||
}
|
||||
|
||||
const data = await resolveOpenAPIWebhook(filesystem, {
|
||||
@@ -50,7 +50,7 @@ async function baseResolveOpenAPIWebhookBlock(
|
||||
method: block.data.method,
|
||||
});
|
||||
|
||||
return { data, specUrl };
|
||||
return { data, specUrl, publicURL };
|
||||
} catch (error) {
|
||||
if (error instanceof OpenAPIParseError) {
|
||||
return { error };
|
||||
|
||||
@@ -38,6 +38,8 @@ export type FetchOpenAPIFilesystemResult =
|
||||
error?: undefined;
|
||||
filesystem: Filesystem<OpenAPIV3xDocument> | null;
|
||||
specUrl: string | null;
|
||||
/** Public URL of the OpenAPI spec, used as the spec URL for Scalar's "Test it" modal */
|
||||
publicURL: string | null;
|
||||
}
|
||||
| FetchOpenAPIFilesystemError;
|
||||
|
||||
@@ -48,13 +50,14 @@ type FetchOpenAPIFilesystemError = {
|
||||
error: OpenAPIParseError;
|
||||
filesystem?: undefined;
|
||||
specUrl?: undefined;
|
||||
publicURL?: undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolved OpenAPI block result.
|
||||
*/
|
||||
export type ResolveOpenAPIBlockResult<T> =
|
||||
| { error?: undefined; data: T | null; specUrl: string | null }
|
||||
| { error?: undefined; data: T | null; specUrl: string | null; publicURL: string | null }
|
||||
| ResolveOpenAPIBlockError;
|
||||
|
||||
/**
|
||||
@@ -64,4 +67,5 @@ type ResolveOpenAPIBlockError = {
|
||||
error: OpenAPIParseError;
|
||||
data?: undefined;
|
||||
specUrl?: undefined;
|
||||
publicURL?: undefined;
|
||||
};
|
||||
|
||||
@@ -58,12 +58,17 @@ export interface ResolvedContentRef {
|
||||
context: GitBookSpaceContext;
|
||||
revisionReusableContent: RevisionReusableContent;
|
||||
};
|
||||
/** Resolve OpenAPI spec filesystem. */
|
||||
openAPIFilesystem?: Filesystem;
|
||||
/**
|
||||
* Space that the content ref belongs to (if applicable).
|
||||
*/
|
||||
space?: Space;
|
||||
/** Resolved OpenAPI spec, if the reference is an OpenAPI spec. */
|
||||
openapi?: {
|
||||
/** OpenAPI spec filesystem. */
|
||||
filesystem: Filesystem;
|
||||
/** Public URL of the OpenAPI spec */
|
||||
publicURL: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResolveContentRefOptions {
|
||||
@@ -340,13 +345,13 @@ export async function resolveContentRef(
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
// @ts-expect-error - Backward compatibility: `urls.source` replaces `url` in the next API version
|
||||
href: openAPISpecVersionContent.urls?.source ?? openAPISpecVersionContent.url,
|
||||
href: openAPISpecVersionContent.urls.source,
|
||||
text: contentRef.spec,
|
||||
active: false,
|
||||
openAPIFilesystem: openAPISpecVersionContent.filesystem as Filesystem,
|
||||
// @ts-expect-error - Public URL is not yet supported in the API
|
||||
publicURL: openAPISpecVersionContent.urls?.public,
|
||||
openapi: {
|
||||
filesystem: openAPISpecVersionContent.filesystem as Filesystem,
|
||||
publicURL: openAPISpecVersionContent.urls.public,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ function OpenAPICodeSampleFooter(props: {
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
{!hideTryItPanel && hasValidHost && (
|
||||
{!hideTryItPanel && hasValidHost && specUrl && (
|
||||
<ScalarApiButton
|
||||
context={resolveScalarClientContext(context, servers, specUrl)}
|
||||
withProxy={Boolean(data.operation['x-enable-proxy'] ?? data['x-enable-proxy'])}
|
||||
|
||||
@@ -67,9 +67,10 @@ export interface OpenAPIContext
|
||||
renderDocument: (props: { document: object }) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Specification URL.
|
||||
* Public specification URL, used by Scalar's "Test it" modal.
|
||||
* When null, the "Test it" button is hidden.
|
||||
*/
|
||||
specUrl: string;
|
||||
specUrl: string | null;
|
||||
|
||||
/**
|
||||
* Build a signed proxy URL that restricts the proxy to specific origins.
|
||||
|
||||
Reference in New Issue
Block a user