mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +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": {
|
||||
"@codemirror/state": "6.4.1",
|
||||
"@gitbook/api": "0.111.0",
|
||||
"@gitbook/api": "0.113.0",
|
||||
"react": "^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=="],
|
||||
|
||||
"@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"],
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"packageManager": "bun@1.2.8",
|
||||
"overrides": {
|
||||
"@codemirror/state": "6.4.1",
|
||||
"@gitbook/api": "0.111.0",
|
||||
"@gitbook/api": "0.113.0",
|
||||
"react": "^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} />,
|
||||
eye: (props) => <Icon icon="eye" {...props} />,
|
||||
lock: (props) => <Icon icon="lock" {...props} />,
|
||||
check: (props) => <Icon icon="check" {...props} />,
|
||||
'check-circle': (props) => <Icon icon="check-circle" {...props} />,
|
||||
},
|
||||
codeBlock: (props) => {
|
||||
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');
|
||||
}
|
||||
|
||||
const resolved = await resolveContentRef(block.data.ref, context.contentContext);
|
||||
if (!resolved?.reusableContent?.document) {
|
||||
const dataFetcher = block.meta?.token
|
||||
? 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;
|
||||
}
|
||||
|
||||
const document = await getDataOrNull(
|
||||
context.contentContext.dataFetcher.getDocument({
|
||||
spaceId: context.contentContext.space.id,
|
||||
documentId: resolved.reusableContent.document,
|
||||
dataFetcher.getDocument({
|
||||
spaceId: resolved.reusableContent.space,
|
||||
documentId: reusableContent.document,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
multiWeight: {
|
||||
@@ -81,6 +84,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
multiSource: {
|
||||
@@ -99,6 +105,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
missingFormat: {
|
||||
@@ -117,6 +126,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
empty: {
|
||||
@@ -124,6 +136,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
custom: true,
|
||||
fontFamily: 'Empty Font',
|
||||
fontFaces: [],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
specialChars: {
|
||||
@@ -136,6 +151,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
complex: {
|
||||
@@ -158,6 +176,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
|
||||
],
|
||||
},
|
||||
],
|
||||
permissions: {
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
|
||||
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
|
||||
*/
|
||||
export function generateFontFacesCSS(customFont: CustomizationFontDefinition): string {
|
||||
export function generateFontFacesCSS(customFont: CustomizationFontDefinitionInput): string {
|
||||
const { fontFaces } = customFont;
|
||||
|
||||
// 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)
|
||||
*/
|
||||
export function getFontSourcesToPreload(customFont: CustomizationFontDefinition) {
|
||||
export function getFontSourcesToPreload(customFont: CustomizationFontDefinitionInput) {
|
||||
return customFont.fontFaces.filter(
|
||||
(face): face is typeof face & { weight: 400 | 700 } =>
|
||||
face.weight === 400 || face.weight === 700
|
||||
|
||||
@@ -267,6 +267,7 @@ export const getPublishedContentByUrl = cache({
|
||||
|
||||
const parsed = parseCacheResponse(response);
|
||||
|
||||
// biome-ignore lint/suspicious/noConsole: log the ttl of the token
|
||||
console.log(
|
||||
`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;
|
||||
/** Page document resolved from the content ref */
|
||||
page?: RevisionPageDocument;
|
||||
/** Resolved reusable content, if the ref points to reusable content on a revision. */
|
||||
reusableContent?: RevisionReusableContent;
|
||||
/** Resolved reusable content, if the ref points to reusable content on a revision. Also contains the space and revision used for resolution. */
|
||||
reusableContent?: {
|
||||
revisionReusableContent: RevisionReusableContent;
|
||||
space: string;
|
||||
revision: string;
|
||||
};
|
||||
/** Resolve OpenAPI spec filesystem. */
|
||||
openAPIFilesystem?: Filesystem;
|
||||
}
|
||||
@@ -231,21 +235,52 @@ export async function resolveContentRef(
|
||||
}
|
||||
|
||||
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(
|
||||
dataFetcher.getReusableContent({
|
||||
spaceId: space.id,
|
||||
revisionId,
|
||||
spaceId: container.space,
|
||||
revisionId: container.revision,
|
||||
reusableContentId: contentRef.reusableContent,
|
||||
})
|
||||
);
|
||||
|
||||
if (!reusableContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
href: getGitBookAppHref(`/s/${space.id}`),
|
||||
href: getGitBookAppHref(`/s/${container.space}/~/reusable/${reusableContent.id}`),
|
||||
text: reusableContent.title,
|
||||
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 { createLinker } from '@v2/lib/links';
|
||||
|
||||
import { GitBookAPI } from '@gitbook/api';
|
||||
import { DataFetcherError, wrapDataFetcherError } from '@v2/lib/data';
|
||||
import { headers } from 'next/headers';
|
||||
import {
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
getUserById,
|
||||
renderIntegrationUi,
|
||||
searchSiteContent,
|
||||
withAPI as withAPIV1,
|
||||
} from './api';
|
||||
import { getDynamicCustomizationSettings } from './customization';
|
||||
import { withLeadingSlash, withTrailingSlash } from './paths';
|
||||
@@ -58,7 +60,7 @@ export async function getV1BaseContext(): Promise<GitBookBaseContext> {
|
||||
return url;
|
||||
};
|
||||
|
||||
const dataFetcher = await getDataFetcherV1();
|
||||
const dataFetcher = getDataFetcherV1();
|
||||
|
||||
const imageResizer = createImageResizer({
|
||||
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.
|
||||
* 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 = {
|
||||
async api() {
|
||||
const result = await api();
|
||||
return result.client;
|
||||
return withAPI(async () => {
|
||||
const result = await api();
|
||||
return result.client;
|
||||
});
|
||||
},
|
||||
|
||||
withToken() {
|
||||
// In v1, the token is global and controlled by the middleware.
|
||||
// We don't need to do anything special here.
|
||||
return dataFetcher;
|
||||
withToken({ apiToken }) {
|
||||
return getDataFetcherV1(apiToken);
|
||||
},
|
||||
|
||||
getUserById(userId) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const user = await getUserById(userId);
|
||||
if (!user) {
|
||||
throw new DataFetcherError('User not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const user = await getUserById(userId);
|
||||
if (!user) {
|
||||
throw new DataFetcherError('User not found', 404);
|
||||
}
|
||||
|
||||
return user;
|
||||
});
|
||||
return user;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getPublishedContentSite(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getPublishedContentSite(params);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getPublishedContentSite(params);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getSpace(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getSpace(params.spaceId, params.shareKey);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getSpace(params.spaceId, params.shareKey);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getChangeRequest(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const changeRequest = await getChangeRequest(
|
||||
params.spaceId,
|
||||
params.changeRequestId
|
||||
);
|
||||
if (!changeRequest) {
|
||||
throw new DataFetcherError('Change request not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const changeRequest = await getChangeRequest(
|
||||
params.spaceId,
|
||||
params.changeRequestId
|
||||
);
|
||||
if (!changeRequest) {
|
||||
throw new DataFetcherError('Change request not found', 404);
|
||||
}
|
||||
|
||||
return changeRequest;
|
||||
});
|
||||
return changeRequest;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevision(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getRevision(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getRevision(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionFile(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const revisionFile = await getRevisionFile(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.fileId
|
||||
);
|
||||
if (!revisionFile) {
|
||||
throw new DataFetcherError('Revision file not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const revisionFile = await getRevisionFile(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.fileId
|
||||
);
|
||||
if (!revisionFile) {
|
||||
throw new DataFetcherError('Revision file not found', 404);
|
||||
}
|
||||
|
||||
return revisionFile;
|
||||
});
|
||||
return revisionFile;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionPageMarkdown() {
|
||||
@@ -160,117 +206,140 @@ async function getDataFetcherV1(): Promise<GitBookDataFetcher> {
|
||||
},
|
||||
|
||||
getDocument(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const document = await getDocument(params.spaceId, params.documentId);
|
||||
if (!document) {
|
||||
throw new DataFetcherError('Document not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const document = await getDocument(params.spaceId, params.documentId);
|
||||
if (!document) {
|
||||
throw new DataFetcherError('Document not found', 404);
|
||||
}
|
||||
|
||||
return document;
|
||||
});
|
||||
return document;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getComputedDocument(params) {
|
||||
return wrapDataFetcherError(() => {
|
||||
return getComputedDocument(
|
||||
params.organizationId,
|
||||
params.spaceId,
|
||||
params.source,
|
||||
params.seed
|
||||
);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(() => {
|
||||
return getComputedDocument(
|
||||
params.organizationId,
|
||||
params.spaceId,
|
||||
params.source,
|
||||
params.seed
|
||||
);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionPages(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
return getRevisionPages(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
return getRevisionPages(params.spaceId, params.revisionId, {
|
||||
metadata: params.metadata,
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getRevisionPageByPath(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const revisionPage = await getRevisionPageByPath(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.path
|
||||
);
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const revisionPage = await getRevisionPageByPath(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.path
|
||||
);
|
||||
|
||||
if (!revisionPage) {
|
||||
throw new DataFetcherError('Revision page not found', 404);
|
||||
}
|
||||
if (!revisionPage) {
|
||||
throw new DataFetcherError('Revision page not found', 404);
|
||||
}
|
||||
|
||||
return revisionPage;
|
||||
});
|
||||
return revisionPage;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getReusableContent(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const reusableContent = await getReusableContent(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.reusableContentId
|
||||
);
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const reusableContent = await getReusableContent(
|
||||
params.spaceId,
|
||||
params.revisionId,
|
||||
params.reusableContentId
|
||||
);
|
||||
|
||||
if (!reusableContent) {
|
||||
throw new DataFetcherError('Reusable content not found', 404);
|
||||
}
|
||||
if (!reusableContent) {
|
||||
throw new DataFetcherError('Reusable content not found', 404);
|
||||
}
|
||||
|
||||
return reusableContent;
|
||||
});
|
||||
return reusableContent;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getLatestOpenAPISpecVersionContent(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const openAPISpecVersionContent = await getLatestOpenAPISpecVersionContent(
|
||||
params.organizationId,
|
||||
params.slug
|
||||
);
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const openAPISpecVersionContent = await getLatestOpenAPISpecVersionContent(
|
||||
params.organizationId,
|
||||
params.slug
|
||||
);
|
||||
|
||||
if (!openAPISpecVersionContent) {
|
||||
throw new DataFetcherError('OpenAPI spec version content not found', 404);
|
||||
}
|
||||
if (!openAPISpecVersionContent) {
|
||||
throw new DataFetcherError('OpenAPI spec version content not found', 404);
|
||||
}
|
||||
|
||||
return openAPISpecVersionContent;
|
||||
});
|
||||
return openAPISpecVersionContent;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
getSiteRedirectBySource(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const siteRedirect = await getSiteRedirectBySource(params);
|
||||
if (!siteRedirect) {
|
||||
throw new DataFetcherError('Site redirect not found', 404);
|
||||
}
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const siteRedirect = await getSiteRedirectBySource(params);
|
||||
if (!siteRedirect) {
|
||||
throw new DataFetcherError('Site redirect not found', 404);
|
||||
}
|
||||
|
||||
return siteRedirect;
|
||||
});
|
||||
return siteRedirect;
|
||||
})
|
||||
);
|
||||
},
|
||||
getEmbedByUrl(params) {
|
||||
return wrapDataFetcherError(() => {
|
||||
return getEmbedByUrlInSpace(params.spaceId, params.url);
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(() => {
|
||||
return getEmbedByUrlInSpace(params.spaceId, params.url);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
searchSiteContent(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const { organizationId, siteId, query, cacheBust, scope } = params;
|
||||
const result = await searchSiteContent(
|
||||
organizationId,
|
||||
siteId,
|
||||
query,
|
||||
scope,
|
||||
cacheBust
|
||||
);
|
||||
return result.items;
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const { organizationId, siteId, query, cacheBust, scope } = params;
|
||||
const result = await searchSiteContent(
|
||||
organizationId,
|
||||
siteId,
|
||||
query,
|
||||
scope,
|
||||
cacheBust
|
||||
);
|
||||
return result.items;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
renderIntegrationUi(params) {
|
||||
return wrapDataFetcherError(async () => {
|
||||
const result = await renderIntegrationUi(params.integrationName, params.request);
|
||||
return result;
|
||||
});
|
||||
return withAPI(() =>
|
||||
wrapDataFetcherError(async () => {
|
||||
const result = await renderIntegrationUi(
|
||||
params.integrationName,
|
||||
params.request
|
||||
);
|
||||
return result;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
streamAIResponse() {
|
||||
|
||||
Reference in New Issue
Block a user