@@ -10,14 +10,12 @@ const { log, mongoLogId } = createLogger('COMPASS-USER-STORAGE');
1010
1111type SerializeContent < I > = ( content : I ) => string ;
1212type DeserializeContent = ( content : string ) => unknown ;
13- type GetFileName = ( id : string ) => string ;
1413
1514export type FileUserDataOptions < Input > = {
1615 subdir : string ;
1716 basePath ?: string ;
1817 serialize ?: SerializeContent < Input > ;
1918 deserialize ?: DeserializeContent ;
20- getFileName ?: GetFileName ;
2119} ;
2220
2321export type AtlasUserDataOptions < Input > = {
@@ -29,45 +27,11 @@ type ReadOptions = {
2927 ignoreErrors : boolean ;
3028} ;
3129
32- // Copied from the Node.js fs module.
33- export interface Stats {
34- isFile ( ) : boolean ;
35- isDirectory ( ) : boolean ;
36- isBlockDevice ( ) : boolean ;
37- isCharacterDevice ( ) : boolean ;
38- isSymbolicLink ( ) : boolean ;
39- isFIFO ( ) : boolean ;
40- isSocket ( ) : boolean ;
41- dev : number ;
42- ino : number ;
43- mode : number ;
44- nlink : number ;
45- uid : number ;
46- gid : number ;
47- rdev : number ;
48- size : number ;
49- blksize : number ;
50- blocks : number ;
51- atimeMs : number ;
52- mtimeMs : number ;
53- ctimeMs : number ;
54- birthtimeMs : number ;
55- atime : Date ;
56- mtime : Date ;
57- ctime : Date ;
58- birthtime : Date ;
59- }
60-
6130export interface ReadAllResult < T extends z . Schema > {
6231 data : z . output < T > [ ] ;
6332 errors : Error [ ] ;
6433}
6534
66- export interface ReadAllWithStatsResult < T extends z . Schema > {
67- data : [ z . output < T > , Stats ] [ ] ;
68- errors : Error [ ] ;
69- }
70-
7135export abstract class IUserData < T extends z . Schema > {
7236 protected readonly validator : T ;
7337 protected readonly serialize : SerializeContent < z . input < T > > ;
@@ -100,7 +64,6 @@ export abstract class IUserData<T extends z.Schema> {
10064export class FileUserData < T extends z . Schema > extends IUserData < T > {
10165 private readonly subdir : string ;
10266 private readonly basePath ?: string ;
103- private readonly getFileName : GetFileName ;
10467 protected readonly semaphore = new Semaphore ( 100 ) ;
10568
10669 constructor (
@@ -110,13 +73,15 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
11073 basePath,
11174 serialize,
11275 deserialize,
113- getFileName = ( id ) => `${ id } .json` ,
11476 } : FileUserDataOptions < z . input < T > >
11577 ) {
11678 super ( validator , { serialize, deserialize } ) ;
11779 this . subdir = subdir ;
11880 this . basePath = basePath ;
119- this . getFileName = getFileName ;
81+ }
82+
83+ private getFileName ( id : string ) {
84+ return `${ id } .json` ;
12085 }
12186
12287 private async getEnsuredBasePath ( ) : Promise < string > {
@@ -148,21 +113,15 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
148113 return path . resolve ( root , pathRelativeToRoot ) ;
149114 }
150115
151- private async readAndParseFileWithStats (
116+ private async readAndParseFile (
152117 absolutePath : string ,
153118 options : ReadOptions
154- ) : Promise < [ z . output < T > , Stats ] | undefined > {
119+ ) : Promise < z . output < T > | undefined > {
155120 let data : string ;
156- let stats : Stats ;
157- let handle : fs . FileHandle | undefined = undefined ;
158121 let release : ( ( ) => void ) | undefined = undefined ;
159122 try {
160123 release = await this . semaphore . waitForRelease ( ) ;
161- handle = await fs . open ( absolutePath , 'r' ) ;
162- [ stats , data ] = await Promise . all ( [
163- handle . stat ( ) ,
164- handle . readFile ( 'utf-8' ) ,
165- ] ) ;
124+ data = await fs . readFile ( absolutePath , 'utf-8' ) ;
166125 } catch ( error ) {
167126 log . error ( mongoLogId ( 1_001_000_234 ) , 'Filesystem' , 'Error reading file' , {
168127 path : absolutePath ,
@@ -173,13 +132,12 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
173132 }
174133 throw error ;
175134 } finally {
176- await handle ?. close ( ) ;
177135 release ?.( ) ;
178136 }
179137
180138 try {
181139 const content = this . deserialize ( data ) ;
182- return [ this . validator . parse ( content ) , stats ] ;
140+ return this . validator . parse ( content ) ;
183141 } catch ( error ) {
184142 log . error ( mongoLogId ( 1_001_000_235 ) , 'Filesystem' , 'Error parsing data' , {
185143 path : absolutePath ,
@@ -235,70 +193,37 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
235193 }
236194 }
237195
238- async readAllWithStats (
196+ async readAll (
239197 options : ReadOptions = {
240198 ignoreErrors : true ,
241199 }
242- ) : Promise < ReadAllWithStatsResult < T > > {
243- const absolutePath = await this . getFileAbsolutePath ( ) ;
244- const filePathList = await fs . readdir ( absolutePath ) ;
245-
246- const data = await Promise . allSettled (
247- filePathList . map ( ( x ) =>
248- this . readAndParseFileWithStats ( path . join ( absolutePath , x ) , options )
249- )
250- ) ;
251-
252- const result : ReadAllWithStatsResult < T > = {
200+ ) : Promise < ReadAllResult < T > > {
201+ const result : ReadAllResult < T > = {
253202 data : [ ] ,
254203 errors : [ ] ,
255204 } ;
256-
257- for ( const item of data ) {
258- if ( item . status === 'fulfilled' && item . value ) {
259- result . data . push ( item . value ) ;
205+ try {
206+ const absolutePath = await this . getFileAbsolutePath ( ) ;
207+ const filePathList = await fs . readdir ( absolutePath ) ;
208+ for ( const settled of await Promise . allSettled (
209+ filePathList . map ( ( x ) => {
210+ return this . readAndParseFile ( path . join ( absolutePath , x ) , options ) ;
211+ } )
212+ ) ) {
213+ if ( settled . status === 'fulfilled' && settled . value ) {
214+ result . data . push ( settled . value ) ;
215+ }
216+ if ( settled . status === 'rejected' ) {
217+ result . errors . push ( settled . reason ) ;
218+ }
260219 }
261- if ( item . status === 'rejected' ) {
262- result . errors . push ( item . reason ) ;
220+ return result ;
221+ } catch ( err ) {
222+ if ( options . ignoreErrors ) {
223+ return result ;
263224 }
225+ throw err ;
264226 }
265-
266- return result ;
267- }
268-
269- async readOneWithStats (
270- id : string ,
271- options ?: { ignoreErrors : false }
272- ) : Promise < [ z . output < T > , Stats ] > ;
273- async readOneWithStats (
274- id : string ,
275- options ?: { ignoreErrors : true }
276- ) : Promise < [ z . output < T > , Stats ] | undefined > ;
277- async readOneWithStats (
278- id : string ,
279- options ?: ReadOptions
280- ) : Promise < [ z . output < T > , Stats ] | undefined > ;
281- async readOneWithStats (
282- id : string ,
283- options : ReadOptions = {
284- ignoreErrors : true ,
285- }
286- ) {
287- const filepath = this . getFileName ( id ) ;
288- const absolutePath = await this . getFileAbsolutePath ( filepath ) ;
289- return await this . readAndParseFileWithStats ( absolutePath , options ) ;
290- }
291-
292- async readAll (
293- options : ReadOptions = {
294- ignoreErrors : true ,
295- }
296- ) : Promise < ReadAllResult < T > > {
297- const result = await this . readAllWithStats ( options ) ;
298- return {
299- data : result . data . map ( ( [ data ] ) => data ) ,
300- errors : result . errors ,
301- } ;
302227 }
303228
304229 async readOne (
@@ -319,7 +244,9 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
319244 ignoreErrors : true ,
320245 }
321246 ) {
322- return ( await this . readOneWithStats ( id , options ) ) ?. [ 0 ] ;
247+ const filepath = this . getFileName ( id ) ;
248+ const absolutePath = await this . getFileAbsolutePath ( filepath ) ;
249+ return await this . readAndParseFile ( absolutePath , options ) ;
323250 }
324251
325252 async updateAttributes (
0 commit comments