Skip to content

Commit 31396d8

Browse files
authored
Adapt GBO cache logic to support users sessions info (#2606)
1 parent 664debc commit 31396d8

File tree

9 files changed

+192
-55
lines changed

9 files changed

+192
-55
lines changed

bun.lockb

100755100644
File mode changed.

packages/gitbook/src/app/(site)/(content)/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default async function ContentLayout(props: { children: React.ReactNode }
4242
sections,
4343
} = await fetchContentData();
4444

45-
ReactDOM.preconnect(api().endpoint);
45+
ReactDOM.preconnect(api().client.endpoint);
4646
if (assetsDomain) {
4747
ReactDOM.preconnect(assetsDomain);
4848
}

packages/gitbook/src/components/PageBody/PageBody.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export function PageBody(props: {
148148
sitePointer={pointer}
149149
spaceId={space.id}
150150
pageId={page.id}
151-
apiHost={api().endpoint}
151+
apiHost={api().client.endpoint}
152152
/>
153153
) : null}
154154
</>

packages/gitbook/src/components/PageFeedback/server-actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export async function postPageFeedback(args: {
1818
`No siteSpaceId in pointer. organizationId: ${organizationId}, siteId: ${siteId}, pageId: ${args.pageId}`,
1919
);
2020

21-
await api().orgs.createSitesPageFeedback(
21+
await api().client.orgs.createSitesPageFeedback(
2222
organizationId,
2323
siteId,
2424
siteSpaceId,

packages/gitbook/src/components/Search/server-actions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export const streamAskQuestion = streamResponse(async function* (
158158
siteSpaceId: string | null,
159159
question: string,
160160
) {
161-
const stream = api.api().orgs.streamAskInSite(
161+
const stream = api.api().client.orgs.streamAskInSite(
162162
organizationId,
163163
siteId,
164164
{

packages/gitbook/src/lib/api.ts

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,30 @@ const cacheTtl_1day = {
8787
ttl: 24 * 60 * 60,
8888
};
8989

90-
const apiSyncStorage = new AsyncLocalStorage<GitBookAPI>();
90+
export type GitBookAPIContext = {
91+
/**
92+
* Instance of the GitBook API client.
93+
*/
94+
client: GitBookAPI;
95+
96+
/**
97+
* Context ID representing a hash of the visitor's attributes/assertions that are
98+
* included in the claims property of the content API JWT token.
99+
*
100+
* It serves as a suffix for the cache key to ensure that the content cache is invalidated
101+
* when these attributees/assertions change.
102+
*/
103+
contextId: string | undefined;
104+
};
105+
106+
const apiSyncStorage = new AsyncLocalStorage<GitBookAPIContext>();
91107

92108
export const DEFAULT_API_ENDPOINT = process.env.GITBOOK_API_URL ?? 'https://api.gitbook.com';
93109

94110
/**
95111
* Create a new API client with a token.
96112
*/
97-
export function apiWithToken(apiToken: string): GitBookAPI {
113+
export function apiWithToken(apiToken: string, contextId: string | undefined): GitBookAPIContext {
98114
const headersList = headers();
99115
const apiEndpoint = headersList.get('x-gitbook-api') ?? DEFAULT_API_ENDPOINT;
100116

@@ -104,34 +120,35 @@ export function apiWithToken(apiToken: string): GitBookAPI {
104120
userAgent: userAgent(),
105121
});
106122

107-
return gitbook;
123+
return { client: gitbook, contextId };
108124
}
109125

110126
/**
111127
* Create an API client for the current request.
112128
*/
113-
export function api(): GitBookAPI {
129+
export function api(): GitBookAPIContext {
114130
const existing = apiSyncStorage.getStore();
115131
if (existing) {
116132
return existing;
117133
}
118134

119135
const headersList = headers();
120136
const apiToken = headersList.get('x-gitbook-token');
137+
const contextId = headersList.get('x-gitbook-token-context') ?? undefined;
121138

122139
if (!apiToken) {
123140
throw new Error(
124141
'Missing GitBook API token, please check that the request is correctly processed by the middleware',
125142
);
126143
}
127144

128-
return apiWithToken(apiToken);
145+
return apiWithToken(apiToken, contextId);
129146
}
130147

131148
/**
132149
* Use an API client for an async function.
133150
*/
134-
export function withAPI<T>(client: GitBookAPI, fn: () => Promise<T>): Promise<T> {
151+
export function withAPI<T>(client: GitBookAPIContext, fn: () => Promise<T>): Promise<T> {
135152
return apiSyncStorage.run(client, fn);
136153
}
137154

@@ -164,7 +181,7 @@ export const getUserById = cache({
164181
}),
165182
get: async (userId: string, options: CacheFunctionOptions) => {
166183
try {
167-
const response = await api().users.getUserById(userId, {
184+
const response = await api().client.users.getUserById(userId, {
168185
signal: options.signal,
169186
...noCacheFetchOptions,
170187
});
@@ -200,7 +217,7 @@ export const getPublishedContentByUrl = cache({
200217
options: CacheFunctionOptions,
201218
) => {
202219
try {
203-
const response = await api().urls.getPublishedContentByUrl(
220+
const response = await api().client.urls.getPublishedContentByUrl(
204221
{
205222
url,
206223
visitorAuthToken,
@@ -249,7 +266,7 @@ export const getSpace = cache({
249266
name: 'api.getSpace',
250267
tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
251268
get: async (spaceId: string, shareKey: string | undefined, options: CacheFunctionOptions) => {
252-
const response = await api().spaces.getSpaceById(
269+
const response = await api().client.spaces.getSpaceById(
253270
spaceId,
254271
{
255272
shareKey,
@@ -273,7 +290,7 @@ export const getChangeRequest = cache({
273290
tag: (spaceId, changeRequestId) =>
274291
getAPICacheTag({ tag: 'change-request', space: spaceId, changeRequest: changeRequestId }),
275292
get: async (spaceId: string, changeRequestId: string, options: CacheFunctionOptions) => {
276-
const response = await api().spaces.getChangeRequestById(spaceId, changeRequestId, {
293+
const response = await api().client.spaces.getChangeRequestById(spaceId, changeRequestId, {
277294
...noCacheFetchOptions,
278295
signal: options.signal,
279296
});
@@ -301,13 +318,14 @@ export const getRevision = cache({
301318
name: 'api.getRevision.v2',
302319
tag: (spaceId, revisionId) =>
303320
getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
321+
getKeySuffix: () => api().contextId,
304322
get: async (
305323
spaceId: string,
306324
revisionId: string,
307325
fetchOptions: GetRevisionOptions,
308326
options: CacheFunctionOptions,
309327
) => {
310-
const response = await api().spaces.getRevisionById(
328+
const response = await api().client.spaces.getRevisionById(
311329
spaceId,
312330
revisionId,
313331
{
@@ -331,13 +349,14 @@ export const getRevisionPages = cache({
331349
name: 'api.getRevisionPages.v4',
332350
tag: (spaceId, revisionId) =>
333351
getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
352+
getKeySuffix: () => api().contextId,
334353
get: async (
335354
spaceId: string,
336355
revisionId: string,
337356
fetchOptions: GetRevisionOptions,
338357
options: CacheFunctionOptions,
339358
) => {
340-
const response = await api().spaces.listPagesInRevisionById(
359+
const response = await api().client.spaces.listPagesInRevisionById(
341360
spaceId,
342361
revisionId,
343362
{
@@ -364,6 +383,7 @@ export const getRevisionPageByPath = cache({
364383
name: 'api.getRevisionPageByPath.v3',
365384
tag: (spaceId, revisionId) =>
366385
getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
386+
getKeySuffix: () => api().contextId,
367387
get: async (
368388
spaceId: string,
369389
revisionId: string,
@@ -373,7 +393,7 @@ export const getRevisionPageByPath = cache({
373393
const encodedPath = encodeURIComponent(pagePath);
374394

375395
try {
376-
const response = await api().spaces.getPageInRevisionByPath(
396+
const response = await api().client.spaces.getPageInRevisionByPath(
377397
spaceId,
378398
revisionId,
379399
encodedPath,
@@ -416,7 +436,7 @@ const getRevisionFileById = cache({
416436
) => {
417437
try {
418438
const response = await (async () => {
419-
return api().spaces.getFileInRevisionById(
439+
return api().client.spaces.getFileInRevisionById(
420440
spaceId,
421441
revisionId,
422442
fileId,
@@ -445,6 +465,7 @@ const getRevisionReusableContentById = cache({
445465
name: 'api.getRevisionReusableContentById.v1',
446466
tag: (spaceId, revisionId) =>
447467
getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
468+
getKeySuffix: () => api().contextId,
448469
get: async (
449470
spaceId: string,
450471
revisionId: string,
@@ -453,7 +474,7 @@ const getRevisionReusableContentById = cache({
453474
) => {
454475
try {
455476
const response = await (async () => {
456-
return api().spaces.getReusableContentInRevisionById(
477+
return api().client.spaces.getReusableContentInRevisionById(
457478
spaceId,
458479
revisionId,
459480
reusableContentId,
@@ -489,7 +510,7 @@ const getRevisionAllFiles = cache({
489510
get: async (spaceId: string, revisionId: string, options: CacheFunctionOptions) => {
490511
const response = await getAll(
491512
(params) =>
492-
api().spaces.listFilesInRevisionById(
513+
api().client.spaces.listFilesInRevisionById(
493514
spaceId,
494515
revisionId,
495516
{
@@ -600,8 +621,9 @@ export const getDocument = cache({
600621
name: 'api.getDocument.v2',
601622
tag: (spaceId, documentId) =>
602623
getAPICacheTag({ tag: 'document', space: spaceId, document: documentId }),
624+
getKeySuffix: () => api().contextId,
603625
get: async (spaceId: string, documentId: string, options: CacheFunctionOptions) => {
604-
const response = await api().spaces.getDocumentById(
626+
const response = await api().client.spaces.getDocumentById(
605627
spaceId,
606628
documentId,
607629
{
@@ -626,6 +648,7 @@ export const getDocument = cache({
626648
export const getSiteRedirectBySource = cache({
627649
name: 'api.getSiteRedirectBySource',
628650
tag: ({ siteId }) => getAPICacheTag({ tag: 'site', site: siteId }),
651+
getKeySuffix: () => api().contextId,
629652
get: async (
630653
args: {
631654
organizationId: string;
@@ -637,7 +660,7 @@ export const getSiteRedirectBySource = cache({
637660
options: CacheFunctionOptions,
638661
) => {
639662
try {
640-
const response = await api().orgs.getSiteRedirectBySource(
663+
const response = await api().client.orgs.getSiteRedirectBySource(
641664
args.organizationId,
642665
args.siteId,
643666
{
@@ -669,8 +692,9 @@ export const getSiteRedirectBySource = cache({
669692
export const getSite = cache({
670693
name: 'api.getSite',
671694
tag: (organizationId, siteId) => getAPICacheTag({ tag: 'site', site: siteId }),
695+
getKeySuffix: () => api().contextId,
672696
get: async (organizationId: string, siteId: string, options: CacheFunctionOptions) => {
673-
const response = await api().orgs.getSiteById(organizationId, siteId, {
697+
const response = await api().client.orgs.getSiteById(organizationId, siteId, {
674698
...noCacheFetchOptions,
675699
signal: options.signal,
676700
});
@@ -686,6 +710,7 @@ export const getSite = cache({
686710
export const getPublishedContentSite = cache({
687711
name: 'api.getPublishedContentSite',
688712
tag: ({ siteId }) => getAPICacheTag({ tag: 'site', site: siteId }),
713+
getKeySuffix: () => api().contextId,
689714
get: async (
690715
args: {
691716
organizationId: string;
@@ -694,7 +719,7 @@ export const getPublishedContentSite = cache({
694719
},
695720
options: CacheFunctionOptions,
696721
) => {
697-
const response = await api().orgs.getPublishedContentSite(
722+
const response = await api().client.orgs.getPublishedContentSite(
698723
args.organizationId,
699724
args.siteId,
700725
{
@@ -826,7 +851,7 @@ export const getCollection = cache({
826851
name: 'api.getCollection',
827852
tag: (collectionId) => getAPICacheTag({ tag: 'collection', collection: collectionId }),
828853
get: async (collectionId: string, options: CacheFunctionOptions) => {
829-
const response = await api().collections.getCollectionById(collectionId, {
854+
const response = await api().client.collections.getCollectionById(collectionId, {
830855
...noCacheFetchOptions,
831856
signal: options.signal,
832857
});
@@ -844,7 +869,7 @@ export const getCollectionSpaces = cache({
844869
tag: (collectionId) => getAPICacheTag({ tag: 'collection', collection: collectionId }),
845870
get: async (collectionId: string, options: CacheFunctionOptions) => {
846871
const response = await getAll((params) =>
847-
api().collections.listSpacesInCollectionById(collectionId, params, {
872+
api().client.collections.listSpacesInCollectionById(collectionId, params, {
848873
...noCacheFetchOptions,
849874
signal: options.signal,
850875
}),
@@ -896,14 +921,15 @@ export async function getSpaceContentData(
896921
export const searchSpaceContent = cache({
897922
name: 'api.searchSpaceContent',
898923
tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
924+
getKeySuffix: () => api().contextId,
899925
get: async (
900926
spaceId: string,
901927
/** The revision ID is used as a cache bust key, to avoid revalidating lot of cache entries by tags */
902928
revisionId: string,
903929
query: string,
904930
options: CacheFunctionOptions,
905931
) => {
906-
const response = await api().spaces.searchSpaceContent(
932+
const response = await api().client.spaces.searchSpaceContent(
907933
spaceId,
908934
{ query },
909935
{
@@ -921,8 +947,9 @@ export const searchSpaceContent = cache({
921947
export const searchParentContent = cache({
922948
name: 'api.searchParentContent',
923949
tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
950+
getKeySuffix: () => api().contextId,
924951
get: async (parentId: string, query: string, options: CacheFunctionOptions) => {
925-
const response = await api().search.searchContent(
952+
const response = await api().client.search.searchContent(
926953
{ query },
927954
{
928955
...noCacheFetchOptions,
@@ -941,6 +968,7 @@ export const searchParentContent = cache({
941968
export const searchSiteContent = cache({
942969
name: 'api.searchSiteContent',
943970
tag: (organizationId, siteId) => getAPICacheTag({ tag: 'site', site: siteId }),
971+
getKeySuffix: () => api().contextId,
944972
get: async (
945973
organizationId: string,
946974
siteId: string,
@@ -953,7 +981,7 @@ export const searchSiteContent = cache({
953981
cacheBust?: string,
954982
options?: CacheFunctionOptions,
955983
) => {
956-
const response = await api().orgs.searchSiteContent(
984+
const response = await api().client.orgs.searchSiteContent(
957985
organizationId,
958986
siteId,
959987
{
@@ -980,7 +1008,7 @@ export const getRecommendedQuestionsInSpace = cache({
9801008
name: 'api.getRecommendedQuestionsInSpace',
9811009
tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
9821010
get: async (spaceId: string, options: CacheFunctionOptions) => {
983-
const response = await api().spaces.getRecommendedQuestionsInSpace(spaceId, {
1011+
const response = await api().client.spaces.getRecommendedQuestionsInSpace(spaceId, {
9841012
...noCacheFetchOptions,
9851013
signal: options.signal,
9861014
});
@@ -999,7 +1027,7 @@ export const renderIntegrationUi = cache({
9991027
request: RequestRenderIntegrationUI,
10001028
options: CacheFunctionOptions,
10011029
) => {
1002-
const response = await api().integrations.renderIntegrationUiWithPost(
1030+
const response = await api().client.integrations.renderIntegrationUiWithPost(
10031031
integrationName,
10041032
request,
10051033
{
@@ -1018,7 +1046,7 @@ export const renderIntegrationUi = cache({
10181046
export const getEmbedByUrl = cache({
10191047
name: 'api.getEmbedByUrl',
10201048
get: async (url: string, options: CacheFunctionOptions) => {
1021-
const response = await api().urls.getEmbedByUrl(
1049+
const response = await api().client.urls.getEmbedByUrl(
10221050
{ url },
10231051
{
10241052
...noCacheFetchOptions,
@@ -1036,7 +1064,7 @@ export const getEmbedByUrlInSpace = cache({
10361064
name: 'api.getEmbedByUrlInSpace',
10371065
tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
10381066
get: async (spaceId: string, url: string, options: CacheFunctionOptions) => {
1039-
const response = await api().spaces.getEmbedByUrlInSpace(
1067+
const response = await api().client.spaces.getEmbedByUrlInSpace(
10401068
spaceId,
10411069
{ url },
10421070
{

0 commit comments

Comments
 (0)