15
15
*/
16
16
17
17
import { basename , join } from 'path'
18
+ import * as micromatch from 'micromatch'
18
19
import { Client , ClientOptions , CopyConditions } from 'minio'
19
20
import { Arguments , CodedError , REPL , flatten , inBrowser , i18n } from '@kui-shell/core'
20
21
import { DirEntry , FStat , GlobStats , VFS , mount } from '@kui-shell/plugin-bash-like/fs'
@@ -40,8 +41,10 @@ class S3VFSResponder extends S3VFS implements VFS {
40
41
this . client = new Client ( options )
41
42
}
42
43
43
- public async ls ( _ , filepaths : string [ ] ) {
44
- return flatten ( await Promise . all ( filepaths . map ( filepath => this . dirstat ( filepath . replace ( this . s3Prefix , '' ) ) ) ) )
44
+ public async ls ( { parsedOptions } : Parameters < VFS [ 'ls' ] > [ 0 ] , filepaths : string [ ] ) {
45
+ return flatten (
46
+ await Promise . all ( filepaths . map ( filepath => this . dirstat ( filepath . replace ( this . s3Prefix , '' ) , parsedOptions . d ) ) )
47
+ )
45
48
}
46
49
47
50
/** Degenerate case for `ls /s3`: list all buckets */
@@ -71,75 +74,98 @@ class S3VFSResponder extends S3VFS implements VFS {
71
74
} ) )
72
75
}
73
76
74
- /** This is incomplete */
75
- private globToRegExp ( prefix : string , suffixGlob : string ) : RegExp {
76
- return new RegExp ( '^' + prefix + suffixGlob . replace ( / \* / g , '.*' ) + '$' )
77
+ private async listBucketsMatching ( pattern : string ) : Promise < DirEntry [ ] > {
78
+ const allBuckets = await this . listBuckets ( )
79
+ return allBuckets . filter ( _ => micromatch . isMatch ( _ . name , pattern ) )
77
80
}
78
81
79
82
/** Enumerate matching objects */
80
- private listObjects ( filepath : string ) : Promise < DirEntry [ ] > {
81
- const [ , bucketName , prefix , wildcardSuffix ] = filepath . match ( / ( [ ^ / ] + ) \/ ? ( [ ^ * ] * ) ( .* ) / )
83
+ private async listObjects ( filepath : string , dashD ) : Promise < DirEntry [ ] > {
84
+ const [ , bucketName , bucketNameSlash , prefix , wildcardSuffix ] = filepath . match ( / ( [ ^ / * ] + ) ( \/ ? ) ( [ ^ * ] * ) ( .* ) / )
82
85
83
86
const pattern =
84
87
prefix . length === 0 && ( wildcardSuffix . length === 0 || wildcardSuffix === '*' )
85
- ? / .* / // e.g. ls /s3/myBucket
86
- : this . globToRegExp ( prefix , wildcardSuffix ) // e.g. ls /s3/myBucket/he*lo
87
-
88
- return this . listObjectsMatching ( bucketName , prefix , pattern )
88
+ ? '*' // e.g. ls /s3/myBucket
89
+ : wildcardSuffix // e.g. ls /s3/myBucket/he*lo
90
+
91
+ if ( ! bucketNameSlash && dashD ) {
92
+ // ls -d /s3/myBuck*
93
+ return this . listBucketsMatching ( filepath )
94
+ } else if ( ! bucketNameSlash ) {
95
+ // ls /s3/myBuck*
96
+ const buckets = await this . listBucketsMatching ( filepath )
97
+ return flatten (
98
+ await Promise . all ( buckets . map ( bucketEntry => this . listObjectsMatching ( bucketEntry . name , prefix , pattern , true ) ) )
99
+ )
100
+ } else {
101
+ // ls /s3/myBucket/myObj*
102
+ return this . listObjectsMatching ( bucketName , prefix , pattern )
103
+ }
89
104
}
90
105
91
106
/** Enumerate objects with a suffix wildcard, e.g. C* */
92
- private async listObjectsMatching ( bucketName : string , prefix : string , pattern : RegExp ) : Promise < DirEntry [ ] > {
93
- const objectStream = await this . client . listObjects ( bucketName , prefix )
107
+ private async listObjectsMatching (
108
+ bucketName : string ,
109
+ prefix : string ,
110
+ pattern : string ,
111
+ displayFullPath = false
112
+ ) : Promise < DirEntry [ ] > {
113
+ try {
114
+ const objectStream = await this . client . listObjects ( bucketName , prefix )
94
115
95
- return new Promise ( ( resolve , reject ) => {
96
- const objects : DirEntry [ ] = [ ]
116
+ return new Promise ( ( resolve , reject ) => {
117
+ const objects : DirEntry [ ] = [ ]
97
118
98
- objectStream . on ( 'end' , ( ) => resolve ( objects ) )
99
- objectStream . on ( 'close' , ( ) => resolve ( objects ) )
119
+ objectStream . on ( 'end' , ( ) => resolve ( objects ) )
120
+ objectStream . on ( 'close' , ( ) => resolve ( objects ) )
100
121
101
- objectStream . on ( 'error' , err => {
102
- console . error ( 'Error in S3Vfs.listObjects' , err )
103
- const error : CodedError = new Error ( err . message || 'Error listing s3 objects' )
104
- error . code = err [ 'httpstatuscode' ] || err [ 'code' ] // missing types in @types /minio
105
- reject ( error )
106
- } )
122
+ objectStream . on ( 'error' , err => {
123
+ console . error ( 'Error in S3Vfs.listObjects' , err )
124
+ const error : CodedError = new Error ( err . message || 'Error listing s3 objects' )
125
+ error . code = err [ 'httpstatuscode' ] || err [ 'code' ] // missing types in @types /minio
126
+ reject ( error )
127
+ } )
107
128
108
- objectStream . on ( 'data' , ( { name, size, lastModified } ) => {
109
- if ( pattern . test ( name ) ) {
110
- objects . push ( {
111
- name,
112
- path : join ( this . mountPath , bucketName , name ) ,
113
- stats : {
114
- size,
115
- mtimeMs : lastModified . getTime ( ) ,
116
- mode : 0 ,
117
- uid,
118
- gid
119
- } ,
120
- nameForDisplay : name ,
121
- dirent : {
122
- isFile : true ,
123
- isDirectory : false ,
124
- isSymbolicLink : false ,
125
- isSpecial : false ,
126
- isExecutable : false ,
127
- permissions : '' ,
128
- username
129
- }
130
- } )
131
- }
129
+ objectStream . on ( 'data' , ( { name, size, lastModified } ) => {
130
+ if ( ! pattern || micromatch . isMatch ( name , pattern ) ) {
131
+ const path = join ( this . mountPath , bucketName , name )
132
+
133
+ objects . push ( {
134
+ name,
135
+ path,
136
+ stats : {
137
+ size,
138
+ mtimeMs : lastModified . getTime ( ) ,
139
+ mode : 0 ,
140
+ uid,
141
+ gid
142
+ } ,
143
+ nameForDisplay : displayFullPath ? path : name ,
144
+ dirent : {
145
+ isFile : true ,
146
+ isDirectory : false ,
147
+ isSymbolicLink : false ,
148
+ isSpecial : false ,
149
+ isExecutable : false ,
150
+ permissions : '' ,
151
+ username
152
+ }
153
+ } )
154
+ }
155
+ } )
132
156
} )
133
- } )
157
+ } catch ( err ) {
158
+ throw new Error ( err . message )
159
+ }
134
160
}
135
161
136
162
/** Enumerate the objects specified by the given filepath */
137
- private async dirstat ( filepath : string ) : Promise < DirEntry [ ] > {
163
+ private async dirstat ( filepath : string , dashD : boolean ) : Promise < DirEntry [ ] > {
138
164
try {
139
165
if ( filepath . length === 0 ) {
140
166
return this . listBuckets ( )
141
167
} else {
142
- return this . listObjects ( filepath )
168
+ return this . listObjects ( filepath , dashD )
143
169
}
144
170
} catch ( err ) {
145
171
console . error ( 'Error in S3VFS.ls' , err )
0 commit comments