6
6
import * as path from 'path'
7
7
import * as mime from 'mime-types'
8
8
import * as vscode from 'vscode'
9
+ import * as semver from 'semver'
9
10
import { statSync } from 'fs'
10
11
import { S3 } from 'aws-sdk'
11
12
import { getLogger } from '../../shared/logger'
@@ -16,7 +17,7 @@ import { localize } from '../../shared/utilities/vsCodeUtils'
16
17
import { showOutputMessage } from '../../shared/utilities/messages'
17
18
import { createQuickPick , promptUser , verifySinglePickerOutput } from '../../shared/ui/picker'
18
19
import { addCodiconToString } from '../../shared/utilities/textUtilities'
19
- import { S3Client } from '../../shared/clients/s3Client'
20
+ import { Bucket , Folder , S3Client } from '../../shared/clients/s3Client'
20
21
import { createBucketCommand } from './createBucket'
21
22
import { S3BucketNode } from '../explorer/s3BucketNode'
22
23
import { S3FolderNode } from '../explorer/s3FolderNode'
@@ -102,6 +103,12 @@ export async function uploadFileCommand(
102
103
return fileToUploadRequest ( node ! . bucket . name , key , file )
103
104
} )
104
105
)
106
+ if ( node instanceof S3FolderNode ) {
107
+ globals . context . globalState . update ( 'aws.lastUploadedToS3Folder' , {
108
+ bucket : node . bucket ,
109
+ folder : node . folder ,
110
+ } )
111
+ }
105
112
} else {
106
113
while ( true ) {
107
114
const filesToUpload = await getFile ( document )
@@ -145,18 +152,28 @@ export async function uploadFileCommand(
145
152
return
146
153
}
147
154
148
- const bucketName = bucketResponse . Name
155
+ const bucketName = bucketResponse . bucket ! . Name
149
156
if ( ! bucketName ) {
150
157
throw Error ( `bucketResponse is not a S3.Bucket` )
151
158
}
152
159
153
160
uploadRequests . push (
154
161
...filesToUpload . map ( file => {
155
- const key = path . basename ( file . fsPath )
162
+ const key =
163
+ bucketResponse . folder !== undefined
164
+ ? bucketResponse . folder . path + path . basename ( file . fsPath )
165
+ : path . basename ( file . fsPath )
156
166
return fileToUploadRequest ( bucketName , key , file )
157
167
} )
158
168
)
159
169
170
+ if ( bucketResponse . folder ) {
171
+ globals . context . globalState . update ( 'aws.lastUploadedToS3Folder' , {
172
+ bucket : bucketResponse . bucket ,
173
+ folder : bucketResponse . folder ,
174
+ } )
175
+ }
176
+
160
177
break
161
178
}
162
179
}
@@ -280,7 +297,6 @@ async function uploadBatchOfFiles(
280
297
localize ( 'AWS.s3.uploadFile.startUpload' , 'Uploading file {0} to {1}' , fileName , destinationPath ) ,
281
298
outputChannel
282
299
)
283
-
284
300
let remainder = 0
285
301
let lastLoaded = 0
286
302
// TODO: don't use `withProgress`, it makes it hard to have control over the individual outputs
@@ -374,8 +390,14 @@ async function uploadWithProgress(
374
390
return ( request . ongoingUpload = undefined )
375
391
}
376
392
377
- interface BucketQuickPickItem extends vscode . QuickPickItem {
393
+ export interface BucketQuickPickItem extends vscode . QuickPickItem {
378
394
bucket : S3 . Bucket | undefined
395
+ folder ?: Folder | undefined
396
+ }
397
+
398
+ interface SavedFolder {
399
+ bucket : Bucket
400
+ folder : Folder
379
401
}
380
402
381
403
// TODO:: extract and reuse logic from sam deploy wizard (bucket selection)
@@ -391,7 +413,7 @@ export async function promptUserForBucket(
391
413
s3client : S3Client ,
392
414
promptUserFunction = promptUser ,
393
415
createBucket = createBucketCommand
394
- ) : Promise < S3 . Bucket | 'cancel' | 'back' > {
416
+ ) : Promise < BucketQuickPickItem | 'cancel' | 'back' > {
395
417
let allBuckets : S3 . Bucket [ ]
396
418
try {
397
419
allBuckets = await s3client . listAllBuckets ( )
@@ -418,15 +440,77 @@ export async function promptUserForBucket(
418
440
}
419
441
} )
420
442
443
+ const lastTouchedFolder = globals . context . globalState . get < SavedFolder | undefined > ( 'aws.lastTouchedS3Folder' )
444
+ let lastFolderItem : BucketQuickPickItem | undefined = undefined
445
+ if ( lastTouchedFolder ) {
446
+ lastFolderItem = {
447
+ label : lastTouchedFolder . folder . name ,
448
+ description : '(last opened S3 folder)' ,
449
+ bucket : { Name : lastTouchedFolder . bucket . name } ,
450
+ folder : lastTouchedFolder . folder ,
451
+ }
452
+ }
453
+
454
+ const lastUploadedToFolder = globals . context . globalState . get < SavedFolder | undefined > ( 'aws.lastUploadedToS3Folder' )
455
+ let lastUploadedFolderItem : BucketQuickPickItem | undefined = undefined
456
+ if ( lastUploadedToFolder ) {
457
+ lastUploadedFolderItem = {
458
+ label : lastUploadedToFolder . folder . name ,
459
+ description : '(last uploaded-to S3 folder)' ,
460
+ bucket : { Name : lastUploadedToFolder . bucket . name } ,
461
+ folder : lastUploadedToFolder . folder ,
462
+ }
463
+ }
464
+
465
+ const folderItems = [ ]
466
+ if ( lastUploadedFolderItem !== undefined ) {
467
+ folderItems . push ( lastUploadedFolderItem )
468
+ }
469
+ // de-dupe if folders are the same
470
+ if (
471
+ lastFolderItem !== undefined &&
472
+ ( lastUploadedFolderItem === undefined || lastFolderItem . folder ?. path !== lastUploadedFolderItem . folder ?. path )
473
+ ) {
474
+ folderItems . push ( lastFolderItem )
475
+ }
476
+
477
+ // Remove this stub after we bump minimum to vscode 1.64
478
+ const QuickPickItemKind = semver . gte ( vscode . version , '1.64.0' ) ? ( vscode as any ) . QuickPickItemKind : undefined
479
+ const items : BucketQuickPickItem [ ] = [
480
+ // vscode 1.64 supports QuickPickItemKind.Separator.
481
+ // https://github.com/microsoft/vscode/commit/eb416b4f9ebfda1c798aa7c8b2f4e81c6ce1984f
482
+ ...( QuickPickItemKind && folderItems . length > 0
483
+ ? [
484
+ {
485
+ label : localize ( 'AWS.s3.uploadFile.folderSeparator' , 'Folders' ) ,
486
+ kind : QuickPickItemKind . Separator ,
487
+ bucket : undefined ,
488
+ } as BucketQuickPickItem ,
489
+ ]
490
+ : [ ] ) ,
491
+ ...folderItems ,
492
+ ...( ! QuickPickItemKind
493
+ ? [ ]
494
+ : [
495
+ {
496
+ label : localize ( 'AWS.s3.uploadFile.bucketSeparator' , 'Buckets' ) ,
497
+ kind : QuickPickItemKind . Separator ,
498
+ bucket : undefined ,
499
+ } as BucketQuickPickItem ,
500
+ ] ) ,
501
+ ...bucketItems ,
502
+ createNewBucket ,
503
+ ]
504
+
421
505
const picker = createQuickPick ( {
422
506
options : {
423
507
canPickMany : false ,
424
508
ignoreFocusOut : true ,
425
- title : localize ( 'AWS.message.selectBucket' , 'Select an S3 bucket to upload to' ) ,
509
+ title : localize ( 'AWS.message.selectBucket' , 'Select an S3 bucket or folder to upload to' ) ,
426
510
step : 2 ,
427
511
totalSteps : 2 ,
428
512
} ,
429
- items : [ ... bucketItems , createNewBucket ] ,
513
+ items,
430
514
buttons : [ vscode . QuickInputButtons . Back ] ,
431
515
} )
432
516
const response = verifySinglePickerOutput (
@@ -459,7 +543,7 @@ export async function promptUserForBucket(
459
543
return promptUserForBucket ( s3client )
460
544
}
461
545
} else {
462
- return response . bucket
546
+ return response
463
547
}
464
548
return 'cancel'
465
549
}
0 commit comments