11import type { GraphRest } from "../graph/graphRest" ;
22import type { Archivo } from "../models/archivos" ;
33
4+ type GraphPaged < T > = {
5+ value : T [ ] ;
6+ "@odata.nextLink" ?: string ;
7+ } ;
8+
9+ function toRelativePath ( nextLink : string ) : string {
10+ const u = new URL ( nextLink ) ;
11+
12+ // u.pathname normalmente es "/v1.0/drives/..."
13+ // tu wrapper ya tiene base ".../v1.0/", así que quitamos ese prefijo
14+ const p = u . pathname . replace ( / ^ \/ v 1 \. 0 / i, "" ) ;
15+
16+ return p + u . search ; // queda "/drives/...?$skiptoken=..."
17+ }
18+
19+ function mapToArchivo ( item : any ) : Archivo {
20+ return {
21+ id : item . id ,
22+ name : item . name ,
23+ webUrl : item . webUrl ,
24+ isFolder : ! ! item . folder ,
25+ size : item . size ,
26+ lastModified : item . lastModifiedDateTime ,
27+ childCount : item . folder ?. childCount ?? undefined ,
28+ created : item . createdDateTime ,
29+ } ;
30+ }
31+
432class BibliotecaBaseService {
533 protected graph : GraphRest ;
634 protected hostname : string ;
@@ -73,101 +101,110 @@ class BibliotecaBaseService {
73101 }
74102 }
75103
76- // Listar archivos de una carpeta (por si lo necesitas)
104+ private encodePath ( p : string ) {
105+ const clean = ( p ?? "" ) . replace ( / ^ \/ | \/ $ / g, "" ) ;
106+ if ( ! clean ) return "" ;
107+ return clean
108+ . split ( "/" )
109+ . map ( ( s ) => encodeURIComponent ( s ) )
110+ . join ( "/" ) ;
111+ }
112+
113+ // =========================
114+ // LISTAR / PAGINAR
115+ // =========================
116+
117+ // Listar archivos de una carpeta (por ruta)
77118 async getFilesInFolder ( folderPath : string ) : Promise < Archivo [ ] > {
78119 await this . ensureIds ( ) ;
79120
80- const cleanFolder = folderPath . replace ( / ^ \/ | \/ $ / g, "" ) ;
81- let url : string ;
121+ const encodedPath = this . encodePath ( folderPath ) ;
122+ let url =
123+ encodedPath . length > 0
124+ ? `/drives/${ this . driveId } /root:/${ encodedPath } :/children?$top=200`
125+ : `/drives/${ this . driveId } /root/children?$top=200` ;
82126
83- if ( cleanFolder . length > 0 ) {
84- const segments = cleanFolder . split ( "/" ) . map ( ( s ) => encodeURIComponent ( s ) ) ;
85- const encodedPath = segments . join ( "/" ) ;
127+ const all : any [ ] = [ ] ;
86128
87- url = `/drives/${ this . driveId } /root:/${ encodedPath } :/children` ;
88- } else {
89- url = `/drives/${ this . driveId } /root/children` ;
129+ while ( url ) {
130+ const res = await this . graph . get < GraphPaged < any > > ( url ) ;
131+ all . push ( ...( res . value ?? [ ] ) ) ;
132+ const next = res [ "@odata.nextLink" ] ;
133+ url = next ? toRelativePath ( next ) : "" ;
90134 }
91135
92- const res = await this . graph . get < any > ( url ) ;
93-
94- return ( res . value ?? [ ] ) . map ( ( item : any ) => ( {
95- id : item . id ,
96- name : item . name ,
97- webUrl : item . webUrl ,
98- isFolder : ! ! item . folder ,
99- size : item . size ,
100- lastModified : item . lastModifiedDateTime ,
101- childCount : item . folder ?. childCount ?? undefined ,
102- created : item . createdDateTime ,
103- } ) ) ;
136+ return all . map ( mapToArchivo ) ;
104137 }
105- async findFolderByDocNumber ( docNumber : string ) : Promise < Archivo | null > {
106- await this . ensureIds ( ) ;
107-
108- const baseFolder = "Colaboradores Activos" ; // O el nombre EXACTO de esa carpeta
109- const cleanFolder = baseFolder . replace ( / ^ \/ | \/ $ / g, "" ) ;
110- const segments = cleanFolder . split ( "/" ) . map ( ( s ) => encodeURIComponent ( s ) ) ;
111- const encodedPath = segments . join ( "/" ) ;
112138
113- // Traemos los hijos directos de "Colaboradores Activos"
114- const res = await this . graph . get < any > (
115- `/drives/${ this . driveId } /root:/${ encodedPath } :/children`
116- ) ;
117-
118- const items : any [ ] = res . value ?? [ ] ;
139+ // Listar archivos de una carpeta (por ID) - con paginación
140+ async getFilesByFolderId ( folderId : string ) : Promise < Archivo [ ] > {
141+ await this . ensureIds ( ) ;
119142
120- const folder = items . find ( ( item ) => {
121- const isFolder = ! ! item . folder ;
122- const name : string = item . name ?? "" ;
123- return isFolder && name . startsWith ( `${ docNumber } -` ) ;
124- } ) ;
143+ let url = `/drives/${ this . driveId } /items/${ folderId } /children?$top=200` ;
144+ const all : any [ ] = [ ] ;
125145
126- if ( ! folder ) return null ;
146+ while ( url ) {
147+ const res = await this . graph . get < GraphPaged < any > > ( url ) ;
148+ all . push ( ...( res . value ?? [ ] ) ) ;
149+ const next = res [ "@odata.nextLink" ] ;
150+ url = next ? toRelativePath ( next ) : "" ;
151+ }
127152
128- return {
129- id : folder . id ,
130- name : folder . name ,
131- webUrl : folder . webUrl ,
132- isFolder : ! ! folder . folder ,
133- size : folder . size ,
134- lastModified : folder . lastModified
135-
136- } ;
153+ return all . map ( mapToArchivo ) ;
137154 }
138155
139- async getFilesByFolderId ( folderId : string ) : Promise < Archivo [ ] > {
156+ // Buscar carpeta por número de documento dentro de "Colaboradores Activos"
157+ // (OJO: ahora pagina también, por si hay miles de carpetas)
158+ async findFolderByDocNumber ( docNumber : string ) : Promise < Archivo | null > {
140159 await this . ensureIds ( ) ;
141160
142- const res = await this . graph . get < any > (
143- `/drives/${ this . driveId } /items/${ folderId } /children`
144- ) ;
161+ const baseFolder = "Colaboradores Activos" ;
162+ const encodedBase = this . encodePath ( baseFolder ) ;
163+
164+ let url = `/drives/${ this . driveId } /root:/${ encodedBase } :/children?$top=200` ;
165+
166+ while ( url ) {
167+ const res = await this . graph . get < GraphPaged < any > > ( url ) ;
168+ const items : any [ ] = res . value ?? [ ] ;
169+
170+ const folder = items . find ( ( item ) => {
171+ const isFolder = ! ! item . folder ;
172+ const name : string = item . name ?? "" ;
173+ return isFolder && name . startsWith ( `${ docNumber } -` ) ;
174+ } ) ;
175+
176+ if ( folder ) {
177+ // Importante: aquí usas lastModifiedDateTime (no lastModified)
178+ return {
179+ id : folder . id ,
180+ name : folder . name ,
181+ webUrl : folder . webUrl ,
182+ isFolder : ! ! folder . folder ,
183+ size : folder . size ,
184+ lastModified : folder . lastModifiedDateTime ,
185+ childCount : folder . folder ?. childCount ?? undefined ,
186+ created : folder . createdDateTime ,
187+ } ;
188+ }
145189
146- return ( res . value ?? [ ] ) . map ( ( item : any ) => ( {
147- id : item . id ,
148- name : item . name ,
149- webUrl : item . webUrl ,
150- isFolder : ! ! item . folder ,
151- size : item . size ,
152- lastModified : item . lastModifiedDateTime ,
153- childCount : item . folder ?. childCount ?? undefined ,
154- created : item . createdDateTime ,
155- } ) ) ;
190+ const next = res [ "@odata.nextLink" ] ;
191+ url = next ? toRelativePath ( next ) : "" ;
192+ }
193+
194+ return null ;
156195 }
157196
197+ // =========================
198+ // UPLOAD / RENOMBRE / MOVER
199+ // =========================
158200
159- async uploadFile (
160- folderPath : string ,
161- file : File
162- ) : Promise < Archivo > {
201+ async uploadFile ( folderPath : string , file : File ) : Promise < Archivo > {
163202 await this . ensureIds ( ) ;
164203
165- const cleanFolder = folderPath . replace ( / ^ \/ | \/ $ / g, "" ) ;
204+ const cleanFolder = ( folderPath ?? "" ) . replace ( / ^ \/ | \/ $ / g, "" ) ;
166205 const fileName = file . name ;
167- const serverPath =
168- cleanFolder . length > 0
169- ? `${ cleanFolder } /${ fileName } `
170- : fileName ; // raíz de la biblioteca
206+
207+ const serverPath = cleanFolder . length > 0 ? `${ cleanFolder } /${ fileName } ` : fileName ;
171208
172209 const driveItem = await this . graph . putBinary < any > (
173210 `/drives/${ this . driveId } /root:/${ encodeURI ( serverPath ) } :/content` ,
@@ -182,26 +219,22 @@ class BibliotecaBaseService {
182219 isFolder : ! ! driveItem . folder ,
183220 size : driveItem . size ,
184221 lastModified : driveItem . lastModifiedDateTime ,
222+ childCount : driveItem . folder ?. childCount ?? undefined ,
223+ created : driveItem . createdDateTime ,
185224 } ;
186225 }
187226
188- // Renombrar archivo
189- async renameArchivo ( archivo : Archivo ,
190- nuevoNombreSinExtension : string
191- ) : Promise < Archivo > {
227+ async renameArchivo ( archivo : Archivo , nuevoNombreSinExtension : string ) : Promise < Archivo > {
192228 await this . ensureIds ( ) ;
193229
194- // Mantener extensión original si NO es carpeta
195230 let ext = "" ;
196231 if ( ! archivo . isFolder ) {
197232 const dot = archivo . name . lastIndexOf ( "." ) ;
198- if ( dot > 0 ) {
199- ext = archivo . name . slice ( dot ) ; // incluye el punto, ej: ".pdf"
200- }
233+ if ( dot > 0 ) ext = archivo . name . slice ( dot ) ;
201234 }
202235
203236 const newName = `${ nuevoNombreSinExtension } ${ ext } ` ;
204- // PATCH al driveItem
237+
205238 const item = await this . graph . patch < any > (
206239 `/drives/${ this . driveId } /items/${ archivo . id } ` ,
207240 { name : newName }
@@ -214,39 +247,38 @@ class BibliotecaBaseService {
214247 isFolder : ! ! item . folder ,
215248 size : item . size ,
216249 lastModified : item . lastModifiedDateTime ,
250+ childCount : item . folder ?. childCount ?? undefined ,
251+ created : item . createdDateTime ,
217252 } ;
218253 }
219254
220- //Mover carpeta completa
221- async moveFolderByPath ( sourceFolderPath : string , destParentFolderPath : string , opts ?: { newName ?: string } ) : Promise < Archivo > {
255+ async moveFolderByPath (
256+ sourceFolderPath : string ,
257+ destParentFolderPath : string ,
258+ opts ?: { newName ?: string }
259+ ) : Promise < Archivo > {
222260 await this . ensureIds ( ) ;
223261
224262 const enc = ( p : string ) =>
225- p . replace ( / ^ \/ | \/ $ / g, "" )
263+ ( p ?? "" )
264+ . replace ( / ^ \/ | \/ $ / g, "" )
226265 . split ( "/" )
227266 . map ( encodeURIComponent )
228267 . join ( "/" ) ;
229268
230269 const srcPath = enc ( sourceFolderPath ) ;
231270 const dstPath = enc ( destParentFolderPath ) ;
232271
233- // 1) Resolver item origen (carpeta a mover)
234- const src = await this . graph . get < any > (
235- `/drives/${ this . driveId } /root:/${ srcPath } `
236- ) ;
272+ const src = await this . graph . get < any > ( `/drives/${ this . driveId } /root:/${ srcPath } ` ) ;
237273 if ( ! src ?. id || ! src ?. folder ) {
238274 throw new Error ( "La ruta origen no es una carpeta válida o no existe." ) ;
239275 }
240276
241- // 2) Resolver item destino (carpeta PADRE)
242- const dst = await this . graph . get < any > (
243- `/drives/${ this . driveId } /root:/${ dstPath } `
244- ) ;
277+ const dst = await this . graph . get < any > ( `/drives/${ this . driveId } /root:/${ dstPath } ` ) ;
245278 if ( ! dst ?. id || ! dst ?. folder ) {
246279 throw new Error ( "La ruta destino no es una carpeta válida o no existe." ) ;
247280 }
248281
249- // 3) Mover (Forma A)
250282 const body : any = { parentReference : { id : dst . id } } ;
251283 const newName = opts ?. newName ?. trim ( ) ;
252284 if ( newName ) body . name = newName ;
@@ -263,13 +295,16 @@ class BibliotecaBaseService {
263295 isFolder : ! ! moved . folder ,
264296 size : moved . size ,
265297 lastModified : moved . lastModifiedDateTime ,
298+ childCount : moved . folder ?. childCount ?? undefined ,
299+ created : moved . createdDateTime ,
266300 } ;
267301 }
268-
269-
270302}
271303
272- // Específicos para cada biblioteca (por si luego quieres métodos extra)
304+ // =========================
305+ // Subclases por biblioteca
306+ // =========================
307+
273308export class ColaboradoresEDMService extends BibliotecaBaseService {
274309 constructor ( graph : GraphRest , hostname : string , sitePath : string , name : string ) {
275310 super ( graph , hostname , sitePath , name ) ;
@@ -294,17 +329,14 @@ export class ColaboradoresDenimService extends BibliotecaBaseService {
294329 }
295330}
296331
297-
298332export class ColaboradoresVisualService extends BibliotecaBaseService {
299333 constructor ( graph : GraphRest , hostname : string , sitePath : string , name : string ) {
300334 super ( graph , hostname , sitePath , name ) ;
301335 }
302336}
303337
304-
305338export class ColaboradoresMetaService extends BibliotecaBaseService {
306339 constructor ( graph : GraphRest , hostname : string , sitePath : string , name : string ) {
307340 super ( graph , hostname , sitePath , name ) ;
308341 }
309342}
310-
0 commit comments