Skip to content

Commit

Permalink
Add delete and download procee in list menu
Browse files Browse the repository at this point in the history
  • Loading branch information
nao1215 committed Feb 3, 2024
1 parent 4d14392 commit 84b9a68
Show file tree
Hide file tree
Showing 5 changed files with 377 additions and 139 deletions.
140 changes: 91 additions & 49 deletions ui/s3hub/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,44 +88,46 @@ func fetchS3KeysCmd(ctx context.Context, app *di.S3App, bucket model.Bucket) tea

// downloadS3BucketMsg is the message that is sent when the user wants to download the S3 bucket.
type downloadS3BucketMsg struct {
downloadedBuckets []model.Bucket
downloadedBuckets model.Bucket
}

// downloadS3BucketCmd downloads the S3 bucket.
func downloadS3BucketCmd(ctx context.Context, app *di.S3App, bucket []model.Bucket) tea.Cmd {
return tea.Cmd(func() tea.Msg {
for _, b := range bucket {
output, err := app.S3ObjectsLister.ListS3Objects(ctx, &usecase.S3ObjectsListerInput{
Bucket: b,
func downloadS3BucketCmd(ctx context.Context, app *di.S3App, bucket model.Bucket) tea.Cmd {
d, err := rand.Int(rand.Reader, big.NewInt(500))
if err != nil {
return func() tea.Msg {
return ui.ErrMsg(fmt.Errorf("failed to start download s3 bucket: %w", err))
}
}
delay := time.Millisecond * time.Duration(d.Int64())

return tea.Tick(delay, func(t time.Time) tea.Msg {
output, err := app.S3ObjectsLister.ListS3Objects(ctx, &usecase.S3ObjectsListerInput{
Bucket: bucket,
})
if err != nil {
return ui.ErrMsg(err)
}

for _, v := range output.Objects {
downloadOutput, err := app.S3ObjectDownloader.DownloadS3Object(ctx, &usecase.S3ObjectDownloaderInput{
Bucket: bucket,
Key: v.S3Key,
})
if err != nil {
return ui.ErrMsg(err)
}

if len(output.Objects) == 0 {
continue
}

for _, v := range output.Objects {
downloadOutput, err := app.S3ObjectDownloader.DownloadS3Object(ctx, &usecase.S3ObjectDownloaderInput{
Bucket: b,
Key: v.S3Key,
})
if err != nil {
return ui.ErrMsg(err)
}

destinationPath := filepath.Clean(filepath.Join(s3hub.DefaultDownloadDirPath, b.String(), v.S3Key.String()))
dir := filepath.Dir(destinationPath)
if !gfile.IsDir(dir) {
if err := os.MkdirAll(dir, 0750); err != nil {
return ui.ErrMsg(fmt.Errorf("can not create directory %s: %w", color.YellowString(dir), err))
}
destinationPath := filepath.Clean(filepath.Join(s3hub.DefaultDownloadDirPath, bucket.String(), v.S3Key.String()))
dir := filepath.Dir(destinationPath)
if !gfile.IsDir(dir) {
if err := os.MkdirAll(dir, 0750); err != nil {
return ui.ErrMsg(fmt.Errorf("can not create directory %s: %w", color.YellowString(dir), err))
}
}

if err := downloadOutput.S3Object.ToFile(destinationPath, 0644); err != nil {
return ui.ErrMsg(fmt.Errorf("can not write file to %s: %w", color.YellowString(destinationPath), err))
}
if err := downloadOutput.S3Object.ToFile(destinationPath, 0644); err != nil {
return ui.ErrMsg(fmt.Errorf("can not write file to %s: %w", color.YellowString(destinationPath), err))
}
}
return downloadS3BucketMsg{
Expand All @@ -136,35 +138,42 @@ func downloadS3BucketCmd(ctx context.Context, app *di.S3App, bucket []model.Buck

// downloadS3ObjectsMsg is the message that is sent when the user wants to download the S3 bucket objects.
type downloadS3ObjectsMsg struct {
downloadedS3Key []model.S3Key
downloadedS3Key model.S3Key
}

// downloadS3ObjectsCmd downloads the S3 bucket objects.
func downloadS3ObjectsCmd(ctx context.Context, app *di.S3App, bucket model.Bucket, keys []model.S3Key) tea.Cmd {
return tea.Cmd(func() tea.Msg {
for _, v := range keys {
downloadOutput, err := app.S3ObjectDownloader.DownloadS3Object(ctx, &usecase.S3ObjectDownloaderInput{
Bucket: bucket,
Key: v,
})
if err != nil {
return ui.ErrMsg(err)
}
func downloadS3ObjectsCmd(ctx context.Context, app *di.S3App, bucket model.Bucket, key model.S3Key) tea.Cmd {
d, err := rand.Int(rand.Reader, big.NewInt(500))
if err != nil {
return func() tea.Msg {
return ui.ErrMsg(fmt.Errorf("failed to start download s3 object: %w", err))
}
}
delay := time.Millisecond * time.Duration(d.Int64())

destinationPath := filepath.Clean(filepath.Join(s3hub.DefaultDownloadDirPath, bucket.String(), v.String()))
dir := filepath.Dir(destinationPath)
if !gfile.IsDir(dir) {
if err := os.MkdirAll(dir, 0750); err != nil {
return ui.ErrMsg(fmt.Errorf("can not create directory %s: %w", color.YellowString(dir), err))
}
}
return tea.Tick(delay, func(t time.Time) tea.Msg {
downloadOutput, err := app.S3ObjectDownloader.DownloadS3Object(ctx, &usecase.S3ObjectDownloaderInput{
Bucket: bucket,
Key: key,
})
if err != nil {
return ui.ErrMsg(err)
}

if err := downloadOutput.S3Object.ToFile(destinationPath, 0644); err != nil {
return ui.ErrMsg(fmt.Errorf("can not write file to %s: %w", color.YellowString(destinationPath), err))
destinationPath := filepath.Clean(filepath.Join(s3hub.DefaultDownloadDirPath, bucket.String(), key.String()))
dir := filepath.Dir(destinationPath)
if !gfile.IsDir(dir) {
if err := os.MkdirAll(dir, 0750); err != nil {
return ui.ErrMsg(fmt.Errorf("can not create directory %s: %w", color.YellowString(dir), err))
}
}

if err := downloadOutput.S3Object.ToFile(destinationPath, 0644); err != nil {
return ui.ErrMsg(fmt.Errorf("can not write file to %s: %w", color.YellowString(destinationPath), err))
}

return downloadS3ObjectsMsg{
downloadedS3Key: keys,
downloadedS3Key: key,
}
})
}
Expand Down Expand Up @@ -247,3 +256,36 @@ func divideIntoChunks(slice []model.S3ObjectIdentifier, chunkSize int) [][]model
}
return chunks
}

// deleteS3ObjectMsg is the message that is sent when the user wants to delete the S3 object.
type deleteS3ObjectMsg struct {
deletedS3Key model.S3Key
}

// deleteS3ObjectCmd deletes the S3 object.
func deleteS3ObjectCmd(ctx context.Context, app *di.S3App, bucket model.Bucket, key model.S3Key) tea.Cmd {
d, err := rand.Int(rand.Reader, big.NewInt(500))
if err != nil {
return func() tea.Msg {
return ui.ErrMsg(fmt.Errorf("failed to start deleting s3 bucket: %w", err))
}
}
delay := time.Millisecond * time.Duration(d.Int64())

return tea.Tick(delay, func(t time.Time) tea.Msg {
_, err := app.S3ObjectsDeleter.DeleteS3Objects(ctx, &usecase.S3ObjectsDeleterInput{
Bucket: bucket,
S3ObjectSets: []model.S3ObjectIdentifier{
{
S3Key: key,
},
},
})
if err != nil {
return ui.ErrMsg(err)
}
return deleteS3ObjectMsg{
deletedS3Key: key,
}
})
}
57 changes: 27 additions & 30 deletions ui/s3hub/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,8 @@ func (m *s3hubCreateBucketModel) View() string {
message += ui.ErrorMessage(m.err)
return message
}

if m.status == statusBucketCreated {
return fmt.Sprintf("[ AWS Profile ] %s\n[ Region ] %s\n[ S3 Name ]%s\n\n%s\n\nCreated S3 bucket: %s\n%s\n",
m.awsProfile.String(),
m.region.String(),
m.bucketNameWithColor(),
m.bucketNameLengthString(),
ui.Yellow(m.bucket.String()),
ui.Subtle("<enter>: return to the top"))
}

if m.status == statusBucketCreating {
switch m.status {
case statusBucketCreating:
return fmt.Sprintf("[ AWS Profile ] %s\n[ Region ] %s\n[ %s ]%s\n\n%s\n\n%s\n%s\n\n%s\n",
m.awsProfile.String(),
m.region.String(),
Expand All @@ -170,31 +160,38 @@ func (m *s3hubCreateBucketModel) View() string {
ui.Subtle("<enter>: create bucket"),
"Creating S3 bucket...",
)
}

if m.choice == s3hubCreateBucketRegionChoice {
case statusBucketCreated:
return fmt.Sprintf("[ AWS Profile ] %s\n[ Region ] %s\n[ S3 Name ]%s\n\n%s\n\nCreated S3 bucket: %s\n%s\n",
m.awsProfile.String(),
m.region.String(),
m.bucketNameWithColor(),
m.bucketNameLengthString(),
ui.Yellow(m.bucket.String()),
ui.Subtle("<enter>: return to the top"))
default:
if m.choice == s3hubCreateBucketRegionChoice {
return fmt.Sprintf(
"[ AWS Profile ] %s\n[ ◀︎ %s ▶︎ ] %s\n[ S3 Name ]%s\n\n%s\n\n%s\n%s\n",
m.awsProfile.String(),
ui.Yellow("Region"),
ui.Green(m.region.String()),
m.bucketNameWithColor(),
m.bucketNameLengthString(),
ui.Subtle("<esc>: return to the top | <Ctrl-C>: quit | up/down: select"),
ui.Subtle("<enter>: create bucket | h/l, left/right: select region"),
)
}
return fmt.Sprintf(
"[ AWS Profile ] %s\n[ ◀︎ %s ▶︎ ] %s\n[ S3 Name ]%s\n\n%s\n\n%s\n%s\n",
"[ AWS Profile ] %s\n[ Region ] %s\n[ %s ]%s\n\n%s\n\n%s\n%s\n",
m.awsProfile.String(),
ui.Yellow("Region"),
ui.Green(m.region.String()),
m.region.String(),
ui.Yellow("S3 Name"),
m.bucketNameWithColor(),
m.bucketNameLengthString(),
ui.Subtle("<esc>: return to the top | <Ctrl-C>: quit | up/down: select"),
ui.Subtle("<enter>: create bucket | h/l, left/right: select region"),
ui.Subtle("<enter>: create bucket"),
)
}

return fmt.Sprintf(
"[ AWS Profile ] %s\n[ Region ] %s\n[ %s ]%s\n\n%s\n\n%s\n%s\n",
m.awsProfile.String(),
m.region.String(),
ui.Yellow("S3 Name"),
m.bucketNameWithColor(),
m.bucketNameLengthString(),
ui.Subtle("<esc>: return to the top | <Ctrl-C>: quit | up/down: select"),
ui.Subtle("<enter>: create bucket"),
)
}

// bucketNameWithColor returns the bucket name with color.
Expand Down
32 changes: 14 additions & 18 deletions ui/s3hub/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import (
)

var (
currentBucketNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211"))
doneStyle = lipgloss.NewStyle().Margin(2, 1, 1)
checkMark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓")
currentNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211"))
doneStyle = lipgloss.NewStyle().Margin(2, 1, 1)
checkMark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓")
)

type s3hubDeleteBucketModel struct {
Expand Down Expand Up @@ -92,10 +92,12 @@ func newS3hubDeleteBucketModel() (*s3hubDeleteBucketModel, error) {
}, nil
}

// Init initializes the model.
func (m *s3hubDeleteBucketModel) Init() tea.Cmd {
return nil // Not called this method
}

// Update updates the model based on messages.
func (m *s3hubDeleteBucketModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.err != nil {
return m, tea.Quit
Expand Down Expand Up @@ -184,39 +186,33 @@ func (m *s3hubDeleteBucketModel) View() string {
return ui.ErrorMessage(m.err)
}

if m.status == statusQuit {
switch m.status {
case statusQuit:
return ui.GoodByeMessage()
}

if m.status == statusBucketDeleted {
case statusBucketDeleted:
return doneStyle.Render("All S3 buckets deleted. Press <enter> to return to the top.\n")
}

if m.status == statusBucketDeleting {
case statusBucketDeleting:
w := lipgloss.Width(fmt.Sprintf("%d", m.sum))
bucketCount := fmt.Sprintf(" %*d/%*d", w, m.index, w, m.sum-1)

spin := m.spinner.View() + " "
prog := m.progress.View()
cellsAvail := max(0, m.window.Width-lipgloss.Width(spin+prog+bucketCount))

bucketName := currentBucketNameStyle.Render(m.targetBuckets[0].String())
bucketName := currentNameStyle.Render(m.targetBuckets[0].String())
info := lipgloss.NewStyle().MaxWidth(cellsAvail).Render("Deleting " + bucketName)
cellsRemaining := max(0, m.window.Width-lipgloss.Width(spin+info+prog+bucketCount))
gap := strings.Repeat(" ", cellsRemaining)
return spin + info + gap + prog + bucketCount
}

if m.status == statusBucketFetching || m.status == statusNone {
case statusBucketFetching, statusNone:
return fmt.Sprintf(
"fetching the list of the S3 buckets (profile=%s)\n",
m.awsProfile.String())
}

if m.status == statusBucketFetched {
case statusBucketFetched:
return m.bucketListString()
default:
return m.bucketListString() // TODO: implement
}
return m.bucketListString() // TODO: implement
}

// bucketListString returns the string representation of the bucket list.
Expand Down
Loading

0 comments on commit 84b9a68

Please sign in to comment.