mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 10:03:31 +00:00
Support for reusable content across spaces. (#3173)
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"gitbook-v2": minor
|
||||||
|
"gitbook": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add support for reusable content across spaces.
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@codemirror/state": "6.4.1",
|
"@codemirror/state": "6.4.1",
|
||||||
"@gitbook/api": "0.111.0",
|
"@gitbook/api": "0.113.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
},
|
},
|
||||||
@@ -624,7 +624,7 @@
|
|||||||
|
|
||||||
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
|
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
|
||||||
|
|
||||||
"@gitbook/api": ["@gitbook/api@0.111.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-E5Pk28kPD4p6XNWdwFM9pgDijdByseIZQqcFK+/hoW5tEZa5Yw/plRKJyN1hmwfPL6SKq6Maf0fbIzTQiVXyQQ=="],
|
"@gitbook/api": ["@gitbook/api@0.113.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-PWMeAkdm4bHSl3b5OmtcmskZ6qRkkDhauCPybo8sGnjS03O14YAUtubAQiNCKX/uwbs+yiQ8KRPyeIwn+g42yw=="],
|
||||||
|
|
||||||
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
|
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
"packageManager": "bun@1.2.8",
|
"packageManager": "bun@1.2.8",
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@codemirror/state": "6.4.1",
|
"@codemirror/state": "6.4.1",
|
||||||
"@gitbook/api": "0.111.0",
|
"@gitbook/api": "0.113.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export const contentKitServerContext: ContentKitServerContext = {
|
|||||||
'link-external': (props) => <Icon icon="arrow-up-right-from-square" {...props} />,
|
'link-external': (props) => <Icon icon="arrow-up-right-from-square" {...props} />,
|
||||||
eye: (props) => <Icon icon="eye" {...props} />,
|
eye: (props) => <Icon icon="eye" {...props} />,
|
||||||
lock: (props) => <Icon icon="lock" {...props} />,
|
lock: (props) => <Icon icon="lock" {...props} />,
|
||||||
|
check: (props) => <Icon icon="check" {...props} />,
|
||||||
|
'check-circle': (props) => <Icon icon="check-circle" {...props} />,
|
||||||
},
|
},
|
||||||
codeBlock: (props) => {
|
codeBlock: (props) => {
|
||||||
return <PlainCodeBlock code={props.code} syntax={props.syntax} />;
|
return <PlainCodeBlock code={props.code} syntax={props.syntax} />;
|
||||||
|
|||||||
@@ -13,15 +13,28 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
|
|||||||
throw new Error('Expected a content context to render a reusable content block');
|
throw new Error('Expected a content context to render a reusable content block');
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolved = await resolveContentRef(block.data.ref, context.contentContext);
|
const dataFetcher = block.meta?.token
|
||||||
if (!resolved?.reusableContent?.document) {
|
? context.contentContext.dataFetcher.withToken({ apiToken: block.meta.token })
|
||||||
|
: context.contentContext.dataFetcher;
|
||||||
|
|
||||||
|
const resolved = await resolveContentRef(block.data.ref, {
|
||||||
|
...context.contentContext,
|
||||||
|
dataFetcher,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resolved?.reusableContent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reusableContent = resolved.reusableContent.revisionReusableContent;
|
||||||
|
if (!reusableContent.document) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const document = await getDataOrNull(
|
const document = await getDataOrNull(
|
||||||
context.contentContext.dataFetcher.getDocument({
|
dataFetcher.getDocument({
|
||||||
spaceId: context.contentContext.space.id,
|
spaceId: resolved.reusableContent.space,
|
||||||
documentId: resolved.reusableContent.document,
|
documentId: reusableContent.document,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
multiWeight: {
|
multiWeight: {
|
||||||
@@ -81,6 +84,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
multiSource: {
|
multiSource: {
|
||||||
@@ -99,6 +105,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
missingFormat: {
|
missingFormat: {
|
||||||
@@ -117,6 +126,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
empty: {
|
empty: {
|
||||||
@@ -124,6 +136,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
custom: true,
|
custom: true,
|
||||||
fontFamily: 'Empty Font',
|
fontFamily: 'Empty Font',
|
||||||
fontFaces: [],
|
fontFaces: [],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
specialChars: {
|
specialChars: {
|
||||||
@@ -136,6 +151,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }],
|
sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
complex: {
|
complex: {
|
||||||
@@ -158,6 +176,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
variousURLs: {
|
variousURLs: {
|
||||||
@@ -174,6 +195,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
permissions: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { CustomizationFontDefinition } from '@gitbook/api';
|
import type { CustomizationFontDefinitionInput } from '@gitbook/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the custom font faces and set the --font-custom to the custom font name
|
* Define the custom font faces and set the --font-custom to the custom font name
|
||||||
*/
|
*/
|
||||||
export function generateFontFacesCSS(customFont: CustomizationFontDefinition): string {
|
export function generateFontFacesCSS(customFont: CustomizationFontDefinitionInput): string {
|
||||||
const { fontFaces } = customFont;
|
const { fontFaces } = customFont;
|
||||||
|
|
||||||
// Generate font face declarations for all weights
|
// Generate font face declarations for all weights
|
||||||
@@ -45,7 +45,7 @@ export function generateFontFacesCSS(customFont: CustomizationFontDefinition): s
|
|||||||
/**
|
/**
|
||||||
* Get a list of font sources to preload (only 400 and 700 weights)
|
* Get a list of font sources to preload (only 400 and 700 weights)
|
||||||
*/
|
*/
|
||||||
export function getFontSourcesToPreload(customFont: CustomizationFontDefinition) {
|
export function getFontSourcesToPreload(customFont: CustomizationFontDefinitionInput) {
|
||||||
return customFont.fontFaces.filter(
|
return customFont.fontFaces.filter(
|
||||||
(face): face is typeof face & { weight: 400 | 700 } =>
|
(face): face is typeof face & { weight: 400 | 700 } =>
|
||||||
face.weight === 400 || face.weight === 700
|
face.weight === 400 || face.weight === 700
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ export const getPublishedContentByUrl = cache({
|
|||||||
|
|
||||||
const parsed = parseCacheResponse(response);
|
const parsed = parseCacheResponse(response);
|
||||||
|
|
||||||
|
// biome-ignore lint/suspicious/noConsole: log the ttl of the token
|
||||||
console.log(
|
console.log(
|
||||||
`Parsed ttl: ${parsed.ttl} at ${Date.now()}, for ${'apiToken' in response.data ? response.data.apiToken : '<no-token>'}`
|
`Parsed ttl: ${parsed.ttl} at ${Date.now()}, for ${'apiToken' in response.data ? response.data.apiToken : '<no-token>'}`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -41,8 +41,12 @@ export interface ResolvedContentRef {
|
|||||||
file?: RevisionFile;
|
file?: RevisionFile;
|
||||||
/** Page document resolved from the content ref */
|
/** Page document resolved from the content ref */
|
||||||
page?: RevisionPageDocument;
|
page?: RevisionPageDocument;
|
||||||
/** Resolved reusable content, if the ref points to reusable content on a revision. */
|
/** Resolved reusable content, if the ref points to reusable content on a revision. Also contains the space and revision used for resolution. */
|
||||||
reusableContent?: RevisionReusableContent;
|
reusableContent?: {
|
||||||
|
revisionReusableContent: RevisionReusableContent;
|
||||||
|
space: string;
|
||||||
|
revision: string;
|
||||||
|
};
|
||||||
/** Resolve OpenAPI spec filesystem. */
|
/** Resolve OpenAPI spec filesystem. */
|
||||||
openAPIFilesystem?: Filesystem;
|
openAPIFilesystem?: Filesystem;
|
||||||
}
|
}
|
||||||
@@ -231,21 +235,52 @@ export async function resolveContentRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'reusable-content': {
|
case 'reusable-content': {
|
||||||
|
// Figure out which space and revision the reusable content is in.
|
||||||
|
const container: { space: string; revision: string } | null = await (async () => {
|
||||||
|
// without a space on the content ref, or if the space is the same as the current one, we can use the current revision.
|
||||||
|
if (!contentRef.space || contentRef.space === context.space.id) {
|
||||||
|
return { space: context.space.id, revision: revisionId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const space = await getDataOrNull(
|
||||||
|
dataFetcher.getSpace({
|
||||||
|
spaceId: contentRef.space,
|
||||||
|
shareKey: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!space) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { space: space.id, revision: space.revision };
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (!container) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const reusableContent = await getDataOrNull(
|
const reusableContent = await getDataOrNull(
|
||||||
dataFetcher.getReusableContent({
|
dataFetcher.getReusableContent({
|
||||||
spaceId: space.id,
|
spaceId: container.space,
|
||||||
revisionId,
|
revisionId: container.revision,
|
||||||
reusableContentId: contentRef.reusableContent,
|
reusableContentId: contentRef.reusableContent,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!reusableContent) {
|
if (!reusableContent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
href: getGitBookAppHref(`/s/${space.id}`),
|
href: getGitBookAppHref(`/s/${container.space}/~/reusable/${reusableContent.id}`),
|
||||||
text: reusableContent.title,
|
text: reusableContent.title,
|
||||||
active: false,
|
active: false,
|
||||||
reusableContent,
|
reusableContent: {
|
||||||
|
revisionReusableContent: reusableContent,
|
||||||
|
space: container.space,
|
||||||
|
revision: container.revision,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+193
-124
@@ -8,6 +8,7 @@ import type { GitBookDataFetcher } from '@v2/lib/data/types';
|
|||||||
import { createImageResizer } from '@v2/lib/images';
|
import { createImageResizer } from '@v2/lib/images';
|
||||||
import { createLinker } from '@v2/lib/links';
|
import { createLinker } from '@v2/lib/links';
|
||||||
|
|
||||||
|
import { GitBookAPI } from '@gitbook/api';
|
||||||
import { DataFetcherError, wrapDataFetcherError } from '@v2/lib/data';
|
import { DataFetcherError, wrapDataFetcherError } from '@v2/lib/data';
|
||||||
import { headers } from 'next/headers';
|
import { headers } from 'next/headers';
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +31,7 @@ import {
|
|||||||
getUserById,
|
getUserById,
|
||||||
renderIntegrationUi,
|
renderIntegrationUi,
|
||||||
searchSiteContent,
|
searchSiteContent,
|
||||||
|
withAPI as withAPIV1,
|
||||||
} from './api';
|
} from './api';
|
||||||
import { getDynamicCustomizationSettings } from './customization';
|
import { getDynamicCustomizationSettings } from './customization';
|
||||||
import { withLeadingSlash, withTrailingSlash } from './paths';
|
import { withLeadingSlash, withTrailingSlash } from './paths';
|
||||||
@@ -58,7 +60,7 @@ export async function getV1BaseContext(): Promise<GitBookBaseContext> {
|
|||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
const dataFetcher = await getDataFetcherV1();
|
const dataFetcher = getDataFetcherV1();
|
||||||
|
|
||||||
const imageResizer = createImageResizer({
|
const imageResizer = createImageResizer({
|
||||||
imagesContextId: host,
|
imagesContextId: host,
|
||||||
@@ -82,77 +84,121 @@ export async function getV1BaseContext(): Promise<GitBookBaseContext> {
|
|||||||
* Try not to use this as much as possible, and instead take the data fetcher from the props.
|
* Try not to use this as much as possible, and instead take the data fetcher from the props.
|
||||||
* This data fetcher should only be used at the top of the tree.
|
* This data fetcher should only be used at the top of the tree.
|
||||||
*/
|
*/
|
||||||
async function getDataFetcherV1(): Promise<GitBookDataFetcher> {
|
function getDataFetcherV1(apiTokenOverride?: string): GitBookDataFetcher {
|
||||||
|
let apiClient: GitBookAPI | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a function with the correct API client. If an API token is provided, we
|
||||||
|
* create a new API client with the token. Otherwise, we use the default API client.
|
||||||
|
*/
|
||||||
|
async function withAPI<T>(fn: () => Promise<T>): Promise<T> {
|
||||||
|
// No token override - we can use the default API client.
|
||||||
|
if (!apiTokenOverride) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await api();
|
||||||
|
|
||||||
|
if (!apiClient) {
|
||||||
|
// New client uses same endpoint and user agent as the default client.
|
||||||
|
apiClient = new GitBookAPI({
|
||||||
|
endpoint: client.client.endpoint,
|
||||||
|
authToken: apiTokenOverride,
|
||||||
|
userAgent: client.client.userAgent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return withAPIV1(
|
||||||
|
{
|
||||||
|
client: apiClient,
|
||||||
|
contextId: client.contextId,
|
||||||
|
},
|
||||||
|
fn
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const dataFetcher: GitBookDataFetcher = {
|
const dataFetcher: GitBookDataFetcher = {
|
||||||
async api() {
|
async api() {
|
||||||
const result = await api();
|
return withAPI(async () => {
|
||||||
return result.client;
|
const result = await api();
|
||||||
|
return result.client;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
withToken() {
|
withToken({ apiToken }) {
|
||||||
// In v1, the token is global and controlled by the middleware.
|
return getDataFetcherV1(apiToken);
|
||||||
// We don't need to do anything special here.
|
|
||||||
return dataFetcher;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getUserById(userId) {
|
getUserById(userId) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const user = await getUserById(userId);
|
wrapDataFetcherError(async () => {
|
||||||
if (!user) {
|
const user = await getUserById(userId);
|
||||||
throw new DataFetcherError('User not found', 404);
|
if (!user) {
|
||||||
}
|
throw new DataFetcherError('User not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getPublishedContentSite(params) {
|
getPublishedContentSite(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
return getPublishedContentSite(params);
|
wrapDataFetcherError(async () => {
|
||||||
});
|
return getPublishedContentSite(params);
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getSpace(params) {
|
getSpace(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
return getSpace(params.spaceId, params.shareKey);
|
wrapDataFetcherError(async () => {
|
||||||
});
|
return getSpace(params.spaceId, params.shareKey);
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getChangeRequest(params) {
|
getChangeRequest(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const changeRequest = await getChangeRequest(
|
wrapDataFetcherError(async () => {
|
||||||
params.spaceId,
|
const changeRequest = await getChangeRequest(
|
||||||
params.changeRequestId
|
params.spaceId,
|
||||||
);
|
params.changeRequestId
|
||||||
if (!changeRequest) {
|
);
|
||||||
throw new DataFetcherError('Change request not found', 404);
|
if (!changeRequest) {
|
||||||
}
|
throw new DataFetcherError('Change request not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
return changeRequest;
|
return changeRequest;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevision(params) {
|
getRevision(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
return getRevision(params.spaceId, params.revisionId, {
|
wrapDataFetcherError(async () => {
|
||||||
metadata: params.metadata,
|
return getRevision(params.spaceId, params.revisionId, {
|
||||||
});
|
metadata: params.metadata,
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevisionFile(params) {
|
getRevisionFile(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const revisionFile = await getRevisionFile(
|
wrapDataFetcherError(async () => {
|
||||||
params.spaceId,
|
const revisionFile = await getRevisionFile(
|
||||||
params.revisionId,
|
params.spaceId,
|
||||||
params.fileId
|
params.revisionId,
|
||||||
);
|
params.fileId
|
||||||
if (!revisionFile) {
|
);
|
||||||
throw new DataFetcherError('Revision file not found', 404);
|
if (!revisionFile) {
|
||||||
}
|
throw new DataFetcherError('Revision file not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
return revisionFile;
|
return revisionFile;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevisionPageMarkdown() {
|
getRevisionPageMarkdown() {
|
||||||
@@ -160,117 +206,140 @@ async function getDataFetcherV1(): Promise<GitBookDataFetcher> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDocument(params) {
|
getDocument(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const document = await getDocument(params.spaceId, params.documentId);
|
wrapDataFetcherError(async () => {
|
||||||
if (!document) {
|
const document = await getDocument(params.spaceId, params.documentId);
|
||||||
throw new DataFetcherError('Document not found', 404);
|
if (!document) {
|
||||||
}
|
throw new DataFetcherError('Document not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getComputedDocument(params) {
|
getComputedDocument(params) {
|
||||||
return wrapDataFetcherError(() => {
|
return withAPI(() =>
|
||||||
return getComputedDocument(
|
wrapDataFetcherError(() => {
|
||||||
params.organizationId,
|
return getComputedDocument(
|
||||||
params.spaceId,
|
params.organizationId,
|
||||||
params.source,
|
params.spaceId,
|
||||||
params.seed
|
params.source,
|
||||||
);
|
params.seed
|
||||||
});
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevisionPages(params) {
|
getRevisionPages(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
return getRevisionPages(params.spaceId, params.revisionId, {
|
wrapDataFetcherError(async () => {
|
||||||
metadata: params.metadata,
|
return getRevisionPages(params.spaceId, params.revisionId, {
|
||||||
});
|
metadata: params.metadata,
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevisionPageByPath(params) {
|
getRevisionPageByPath(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const revisionPage = await getRevisionPageByPath(
|
wrapDataFetcherError(async () => {
|
||||||
params.spaceId,
|
const revisionPage = await getRevisionPageByPath(
|
||||||
params.revisionId,
|
params.spaceId,
|
||||||
params.path
|
params.revisionId,
|
||||||
);
|
params.path
|
||||||
|
);
|
||||||
|
|
||||||
if (!revisionPage) {
|
if (!revisionPage) {
|
||||||
throw new DataFetcherError('Revision page not found', 404);
|
throw new DataFetcherError('Revision page not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return revisionPage;
|
return revisionPage;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getReusableContent(params) {
|
getReusableContent(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const reusableContent = await getReusableContent(
|
wrapDataFetcherError(async () => {
|
||||||
params.spaceId,
|
const reusableContent = await getReusableContent(
|
||||||
params.revisionId,
|
params.spaceId,
|
||||||
params.reusableContentId
|
params.revisionId,
|
||||||
);
|
params.reusableContentId
|
||||||
|
);
|
||||||
|
|
||||||
if (!reusableContent) {
|
if (!reusableContent) {
|
||||||
throw new DataFetcherError('Reusable content not found', 404);
|
throw new DataFetcherError('Reusable content not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reusableContent;
|
return reusableContent;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getLatestOpenAPISpecVersionContent(params) {
|
getLatestOpenAPISpecVersionContent(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const openAPISpecVersionContent = await getLatestOpenAPISpecVersionContent(
|
wrapDataFetcherError(async () => {
|
||||||
params.organizationId,
|
const openAPISpecVersionContent = await getLatestOpenAPISpecVersionContent(
|
||||||
params.slug
|
params.organizationId,
|
||||||
);
|
params.slug
|
||||||
|
);
|
||||||
|
|
||||||
if (!openAPISpecVersionContent) {
|
if (!openAPISpecVersionContent) {
|
||||||
throw new DataFetcherError('OpenAPI spec version content not found', 404);
|
throw new DataFetcherError('OpenAPI spec version content not found', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return openAPISpecVersionContent;
|
return openAPISpecVersionContent;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getSiteRedirectBySource(params) {
|
getSiteRedirectBySource(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const siteRedirect = await getSiteRedirectBySource(params);
|
wrapDataFetcherError(async () => {
|
||||||
if (!siteRedirect) {
|
const siteRedirect = await getSiteRedirectBySource(params);
|
||||||
throw new DataFetcherError('Site redirect not found', 404);
|
if (!siteRedirect) {
|
||||||
}
|
throw new DataFetcherError('Site redirect not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
return siteRedirect;
|
return siteRedirect;
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getEmbedByUrl(params) {
|
getEmbedByUrl(params) {
|
||||||
return wrapDataFetcherError(() => {
|
return withAPI(() =>
|
||||||
return getEmbedByUrlInSpace(params.spaceId, params.url);
|
wrapDataFetcherError(() => {
|
||||||
});
|
return getEmbedByUrlInSpace(params.spaceId, params.url);
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
searchSiteContent(params) {
|
searchSiteContent(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const { organizationId, siteId, query, cacheBust, scope } = params;
|
wrapDataFetcherError(async () => {
|
||||||
const result = await searchSiteContent(
|
const { organizationId, siteId, query, cacheBust, scope } = params;
|
||||||
organizationId,
|
const result = await searchSiteContent(
|
||||||
siteId,
|
organizationId,
|
||||||
query,
|
siteId,
|
||||||
scope,
|
query,
|
||||||
cacheBust
|
scope,
|
||||||
);
|
cacheBust
|
||||||
return result.items;
|
);
|
||||||
});
|
return result.items;
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderIntegrationUi(params) {
|
renderIntegrationUi(params) {
|
||||||
return wrapDataFetcherError(async () => {
|
return withAPI(() =>
|
||||||
const result = await renderIntegrationUi(params.integrationName, params.request);
|
wrapDataFetcherError(async () => {
|
||||||
return result;
|
const result = await renderIntegrationUi(
|
||||||
});
|
params.integrationName,
|
||||||
|
params.request
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
streamAIResponse() {
|
streamAIResponse() {
|
||||||
|
|||||||
Reference in New Issue
Block a user