@@ -10,14 +10,12 @@ const { log, mongoLogId } = createLogger('COMPASS-USER-STORAGE');
10
10
11
11
type SerializeContent < I > = ( content : I ) => string ;
12
12
type DeserializeContent = ( content : string ) => unknown ;
13
- type GetFileName = ( id : string ) => string ;
14
13
15
14
export type FileUserDataOptions < Input > = {
16
15
subdir : string ;
17
16
basePath ?: string ;
18
17
serialize ?: SerializeContent < Input > ;
19
18
deserialize ?: DeserializeContent ;
20
- getFileName ?: GetFileName ;
21
19
} ;
22
20
23
21
export type AtlasUserDataOptions < Input > = {
@@ -29,45 +27,11 @@ type ReadOptions = {
29
27
ignoreErrors : boolean ;
30
28
} ;
31
29
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
-
61
30
export interface ReadAllResult < T extends z . Schema > {
62
31
data : z . output < T > [ ] ;
63
32
errors : Error [ ] ;
64
33
}
65
34
66
- export interface ReadAllWithStatsResult < T extends z . Schema > {
67
- data : [ z . output < T > , Stats ] [ ] ;
68
- errors : Error [ ] ;
69
- }
70
-
71
35
export abstract class IUserData < T extends z . Schema > {
72
36
protected readonly validator : T ;
73
37
protected readonly serialize : SerializeContent < z . input < T > > ;
@@ -100,7 +64,6 @@ export abstract class IUserData<T extends z.Schema> {
100
64
export class FileUserData < T extends z . Schema > extends IUserData < T > {
101
65
private readonly subdir : string ;
102
66
private readonly basePath ?: string ;
103
- private readonly getFileName : GetFileName ;
104
67
protected readonly semaphore = new Semaphore ( 100 ) ;
105
68
106
69
constructor (
@@ -110,13 +73,15 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
110
73
basePath,
111
74
serialize,
112
75
deserialize,
113
- getFileName = ( id ) => `${ id } .json` ,
114
76
} : FileUserDataOptions < z . input < T > >
115
77
) {
116
78
super ( validator , { serialize, deserialize } ) ;
117
79
this . subdir = subdir ;
118
80
this . basePath = basePath ;
119
- this . getFileName = getFileName ;
81
+ }
82
+
83
+ private getFileName ( id : string ) {
84
+ return `${ id } .json` ;
120
85
}
121
86
122
87
private async getEnsuredBasePath ( ) : Promise < string > {
@@ -148,21 +113,15 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
148
113
return path . resolve ( root , pathRelativeToRoot ) ;
149
114
}
150
115
151
- private async readAndParseFileWithStats (
116
+ private async readAndParseFile (
152
117
absolutePath : string ,
153
118
options : ReadOptions
154
- ) : Promise < [ z . output < T > , Stats ] | undefined > {
119
+ ) : Promise < z . output < T > | undefined > {
155
120
let data : string ;
156
- let stats : Stats ;
157
- let handle : fs . FileHandle | undefined = undefined ;
158
121
let release : ( ( ) => void ) | undefined = undefined ;
159
122
try {
160
123
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' ) ;
166
125
} catch ( error ) {
167
126
log . error ( mongoLogId ( 1_001_000_234 ) , 'Filesystem' , 'Error reading file' , {
168
127
path : absolutePath ,
@@ -173,13 +132,12 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
173
132
}
174
133
throw error ;
175
134
} finally {
176
- await handle ?. close ( ) ;
177
135
release ?.( ) ;
178
136
}
179
137
180
138
try {
181
139
const content = this . deserialize ( data ) ;
182
- return [ this . validator . parse ( content ) , stats ] ;
140
+ return this . validator . parse ( content ) ;
183
141
} catch ( error ) {
184
142
log . error ( mongoLogId ( 1_001_000_235 ) , 'Filesystem' , 'Error parsing data' , {
185
143
path : absolutePath ,
@@ -235,70 +193,37 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
235
193
}
236
194
}
237
195
238
- async readAllWithStats (
196
+ async readAll (
239
197
options : ReadOptions = {
240
198
ignoreErrors : true ,
241
199
}
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 > = {
253
202
data : [ ] ,
254
203
errors : [ ] ,
255
204
} ;
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
+ }
260
219
}
261
- if ( item . status === 'rejected' ) {
262
- result . errors . push ( item . reason ) ;
220
+ return result ;
221
+ } catch ( err ) {
222
+ if ( options . ignoreErrors ) {
223
+ return result ;
263
224
}
225
+ throw err ;
264
226
}
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
- } ;
302
227
}
303
228
304
229
async readOne (
@@ -319,7 +244,9 @@ export class FileUserData<T extends z.Schema> extends IUserData<T> {
319
244
ignoreErrors : true ,
320
245
}
321
246
) {
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 ) ;
323
250
}
324
251
325
252
async updateAttributes (
0 commit comments