@@ -9,20 +9,19 @@ import {
99 SelectorContextUnavailableError ,
1010 SelectorOptionsUnavailableError ,
1111} from '@/lib/selectors/server/errors'
12- import { flatSelectorResult } from '@/lib/selectors/server/providers/flat-results'
1312import { fetchProviderJson } from '@/lib/selectors/server/providers/provider-http'
1413import {
1514 detailSelectorResult ,
1615 type ExecuteServerSelectorArgs ,
16+ listSelectorResult ,
17+ requireListRequest ,
1718 type ServerSelectorAttachmentMap ,
1819} from '@/lib/selectors/server/types'
1920import type { SafeSelectorOption } from '@/lib/selectors/types'
2021import { assertGraphNextPageUrl , getGraphNextPageUrl } from '@/tools/sharepoint/utils'
2122
2223type SharePointSelectorKey = Extract < ServerSelectorKey , 'sharepoint.lists' | 'sharepoint.sites' >
2324
24- const MAX_GRAPH_PAGES = 10
25-
2625const sharepointCredential = {
2726 kind : 'stored' ,
2827 field : 'oauthCredential' ,
@@ -44,24 +43,43 @@ async function graphToken(args: ExecuteServerSelectorArgs): Promise<string> {
4443 } )
4544}
4645
47- async function drainGraph < T > (
46+ interface GraphPage < T > {
47+ items : T [ ]
48+ nextCursor ?: string
49+ }
50+
51+ function graphPageUrl ( cursor : string | undefined , initialUrl : string ) : string {
52+ if ( ! cursor ) return initialUrl
53+ let cursorUrl : string
54+ try {
55+ cursorUrl = assertGraphNextPageUrl ( cursor )
56+ } catch {
57+ throw new SelectorContextUnavailableError ( )
58+ }
59+ if ( new URL ( cursorUrl ) . pathname !== new URL ( initialUrl ) . pathname ) {
60+ throw new SelectorContextUnavailableError ( )
61+ }
62+ return cursorUrl
63+ }
64+
65+ async function fetchGraphPage < T > (
4866 args : ExecuteServerSelectorArgs ,
4967 initialUrl : string
50- ) : Promise < { values : T [ ] ; truncated : boolean } > {
68+ ) : Promise < GraphPage < T > > {
69+ const request = requireListRequest ( args . selectorKey , args . request )
70+ const requestUrl = graphPageUrl ( request . cursor , initialUrl )
5171 const token = await graphToken ( args )
52- const values : T [ ] = [ ]
53- let nextUrl : string | undefined = initialUrl
54- for ( let page = 0 ; page < MAX_GRAPH_PAGES && nextUrl ; page ++ ) {
55- const data = await fetchProviderJson < { value ?: T [ ] } & Record < string , unknown > > ( nextUrl , {
56- headers : { Authorization : `Bearer ${ token } ` } ,
57- signal : args . signal ,
58- redirect : 'error' ,
59- } )
60- if ( Array . isArray ( data . value ) ) values . push ( ...data . value )
61- const nextLink = getGraphNextPageUrl ( data )
62- nextUrl = nextLink ? assertGraphNextPageUrl ( nextLink ) : undefined
72+ const data = await fetchProviderJson < { value ?: T [ ] } & Record < string , unknown > > ( requestUrl , {
73+ headers : { Authorization : `Bearer ${ token } ` } ,
74+ signal : args . signal ,
75+ redirect : 'error' ,
76+ } )
77+ const nextLink = getGraphNextPageUrl ( data )
78+ const nextCursor = nextLink ? graphPageUrl ( nextLink , initialUrl ) : undefined
79+ return {
80+ items : Array . isArray ( data . value ) ? data . value : [ ] ,
81+ ...( nextCursor ? { nextCursor } : { } ) ,
6382 }
64- return { values, truncated : Boolean ( nextUrl ) }
6583}
6684
6785function requireSiteId ( value : string | undefined ) : string {
@@ -130,7 +148,7 @@ async function getSite(
130148
131149async function listLists ( args : ExecuteServerSelectorArgs ) {
132150 const siteId = requireSiteId ( args . context . siteId )
133- const result = await drainGraph < {
151+ const page = await fetchGraphPage < {
134152 id : string
135153 displayName : string
136154 list ?: { hidden ?: boolean }
@@ -139,21 +157,26 @@ async function listLists(args: ExecuteServerSelectorArgs) {
139157 `https://graph.microsoft.com/v1.0/sites/${ encodeURIComponent ( siteId ) } /lists?$select=id,displayName,description,webUrl,list&$top=999`
140158 )
141159 return {
142- items : result . values
160+ items : page . items
143161 . filter ( ( list ) => list . list ?. hidden !== true )
144162 . map ( ( list ) => ( { id : list . id , label : list . displayName } ) ) ,
145- truncated : result . truncated ,
163+ nextCursor : page . nextCursor ,
146164 }
147165}
148166
149167async function listSites ( args : ExecuteServerSelectorArgs ) {
150- const result = await drainGraph < { id : string ; name : string ; displayName ?: string } > (
168+ const request = requireListRequest ( args . selectorKey , args . request )
169+ const url = new URL ( 'https://graph.microsoft.com/v1.0/sites' )
170+ url . searchParams . set ( 'search' , request . search ?. trim ( ) || '*' )
171+ url . searchParams . set ( '$select' , 'id,name,displayName,webUrl,createdDateTime,lastModifiedDateTime' )
172+ url . searchParams . set ( '$top' , '999' )
173+ const page = await fetchGraphPage < { id : string ; name : string ; displayName ?: string } > (
151174 args ,
152- 'https://graph.microsoft.com/v1.0/sites?search=*&$select=id,name,displayName,webUrl,createdDateTime,lastModifiedDateTime&$top=999'
175+ url . toString ( )
153176 )
154177 return {
155- items : result . values . map ( ( site ) => ( { id : site . id , label : site . displayName || site . name } ) ) ,
156- truncated : result . truncated ,
178+ items : page . items . map ( ( site ) => ( { id : site . id , label : site . displayName || site . name } ) ) ,
179+ nextCursor : page . nextCursor ,
157180 }
158181}
159182
@@ -165,15 +188,8 @@ export const sharepointSelectorAttachments = {
165188 if ( args . request . kind === 'detail' ) {
166189 return detailSelectorResult ( await getList ( args , args . request . id ) )
167190 }
168- const result = await listLists ( args )
169- return flatSelectorResult (
170- args . request ,
171- result . items ,
172- false ,
173- result . truncated
174- ? { truncated : { reason : 'provider-cap' , pages : MAX_GRAPH_PAGES } }
175- : undefined
176- )
191+ const page = await listLists ( args )
192+ return listSelectorResult ( page . items , page . nextCursor )
177193 } ,
178194 } ,
179195 'sharepoint.sites' : {
@@ -183,15 +199,8 @@ export const sharepointSelectorAttachments = {
183199 if ( args . request . kind === 'detail' ) {
184200 return detailSelectorResult ( await getSite ( args , args . request . id ) )
185201 }
186- const result = await listSites ( args )
187- return flatSelectorResult (
188- args . request ,
189- result . items ,
190- false ,
191- result . truncated
192- ? { truncated : { reason : 'provider-cap' , pages : MAX_GRAPH_PAGES } }
193- : undefined
194- )
202+ const page = await listSites ( args )
203+ return listSelectorResult ( page . items , page . nextCursor )
195204 } ,
196205 } ,
197206} satisfies ServerSelectorAttachmentMap < SharePointSelectorKey >
0 commit comments