diff --git a/internal/cli/commands/param/tag/tag.go b/internal/cli/commands/param/tag/tag.go index f28246e1..d8bc9a6b 100644 --- a/internal/cli/commands/param/tag/tag.go +++ b/internal/cli/commands/param/tag/tag.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli/v3" "github.com/mpyw/suve/internal/cli/output" - "github.com/mpyw/suve/internal/infra" + awsparam "github.com/mpyw/suve/internal/provider/aws/param" "github.com/mpyw/suve/internal/usecase/param" ) @@ -57,13 +57,13 @@ func action(ctx context.Context, cmd *cli.Command) error { return err } - client, err := infra.NewParamClient(ctx) + adapter, err := awsparam.NewAdapter(ctx) if err != nil { return fmt.Errorf("failed to initialize AWS client: %w", err) } r := &Runner{ - UseCase: ¶m.TagUseCase{Client: client}, + UseCase: ¶m.TagUseCase{Client: adapter}, Stdout: cmd.Root().Writer, } diff --git a/internal/cli/commands/param/tag/tag_test.go b/internal/cli/commands/param/tag/tag_test.go index a75221c5..5b03f30f 100644 --- a/internal/cli/commands/param/tag/tag_test.go +++ b/internal/cli/commands/param/tag/tag_test.go @@ -6,11 +6,9 @@ import ( "fmt" "testing" - "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mpyw/suve/internal/api/paramapi" appcli "github.com/mpyw/suve/internal/cli/commands" "github.com/mpyw/suve/internal/cli/commands/param/tag" "github.com/mpyw/suve/internal/usecase/param" @@ -56,28 +54,30 @@ func TestCommand_Validation(t *testing.T) { }) } -//nolint:lll // mock struct fields match AWS SDK interface signatures +// mockClient implements provider.ParameterTagger for testing. type mockClient struct { - addTagsFunc func(ctx context.Context, params *paramapi.AddTagsToResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) - removeTagsFunc func(ctx context.Context, params *paramapi.RemoveTagsFromResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) + addTagsFunc func(ctx context.Context, name string, tags map[string]string) error + removeTagsFunc func(ctx context.Context, name string, keys []string) error } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockClient) AddTagsToResource(ctx context.Context, params *paramapi.AddTagsToResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) { +func (m *mockClient) GetTags(_ context.Context, _ string) (map[string]string, error) { + return nil, nil //nolint:nilnil // mock implementation +} + +func (m *mockClient) AddTags(ctx context.Context, name string, tags map[string]string) error { if m.addTagsFunc != nil { - return m.addTagsFunc(ctx, params, optFns...) + return m.addTagsFunc(ctx, name, tags) } - return ¶mapi.AddTagsToResourceOutput{}, nil + return nil } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockClient) RemoveTagsFromResource(ctx context.Context, params *paramapi.RemoveTagsFromResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) { +func (m *mockClient) RemoveTags(ctx context.Context, name string, keys []string) error { if m.removeTagsFunc != nil { - return m.removeTagsFunc(ctx, params, optFns...) + return m.removeTagsFunc(ctx, name, keys) } - return ¶mapi.RemoveTagsFromResourceOutput{}, nil + return nil } func TestRun(t *testing.T) { @@ -97,13 +97,11 @@ func TestRun(t *testing.T) { Tags: map[string]string{"env": "prod"}, }, mock: &mockClient{ - //nolint:lll // inline mock - addTagsFunc: func(_ context.Context, params *paramapi.AddTagsToResourceInput, _ ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) { - assert.Equal(t, "/app/param", lo.FromPtr(params.ResourceId)) - assert.Equal(t, paramapi.ResourceTypeForTaggingParameter, params.ResourceType) - assert.Len(t, params.Tags, 1) + addTagsFunc: func(_ context.Context, name string, tags map[string]string) error { + assert.Equal(t, "/app/param", name) + assert.Len(t, tags, 1) - return ¶mapi.AddTagsToResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -119,11 +117,10 @@ func TestRun(t *testing.T) { Tags: map[string]string{"env": "prod", "team": "backend"}, }, mock: &mockClient{ - //nolint:lll // inline mock - addTagsFunc: func(_ context.Context, params *paramapi.AddTagsToResourceInput, _ ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) { - assert.Len(t, params.Tags, 2) + addTagsFunc: func(_ context.Context, _ string, tags map[string]string) error { + assert.Len(t, tags, 2) - return ¶mapi.AddTagsToResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -138,9 +135,8 @@ func TestRun(t *testing.T) { Tags: map[string]string{"env": "prod"}, }, mock: &mockClient{ - //nolint:lll // inline mock function in test table - addTagsFunc: func(_ context.Context, _ *paramapi.AddTagsToResourceInput, _ ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) { - return nil, fmt.Errorf("AWS error") + addTagsFunc: func(_ context.Context, _ string, _ map[string]string) error { + return fmt.Errorf("AWS error") }, }, wantErr: "failed to add tags", diff --git a/internal/cli/commands/param/untag/untag.go b/internal/cli/commands/param/untag/untag.go index f59feb10..d157002c 100644 --- a/internal/cli/commands/param/untag/untag.go +++ b/internal/cli/commands/param/untag/untag.go @@ -9,7 +9,7 @@ import ( "github.com/urfave/cli/v3" "github.com/mpyw/suve/internal/cli/output" - "github.com/mpyw/suve/internal/infra" + awsparam "github.com/mpyw/suve/internal/provider/aws/param" "github.com/mpyw/suve/internal/usecase/param" ) @@ -50,13 +50,13 @@ func action(ctx context.Context, cmd *cli.Command) error { name := cmd.Args().Get(0) keys := cmd.Args().Slice()[1:] - client, err := infra.NewParamClient(ctx) + adapter, err := awsparam.NewAdapter(ctx) if err != nil { return fmt.Errorf("failed to initialize AWS client: %w", err) } r := &Runner{ - UseCase: ¶m.TagUseCase{Client: client}, + UseCase: ¶m.TagUseCase{Client: adapter}, Stdout: cmd.Root().Writer, } diff --git a/internal/cli/commands/param/untag/untag_test.go b/internal/cli/commands/param/untag/untag_test.go index f8b9c4e7..93383020 100644 --- a/internal/cli/commands/param/untag/untag_test.go +++ b/internal/cli/commands/param/untag/untag_test.go @@ -6,11 +6,9 @@ import ( "fmt" "testing" - "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mpyw/suve/internal/api/paramapi" appcli "github.com/mpyw/suve/internal/cli/commands" "github.com/mpyw/suve/internal/cli/commands/param/untag" "github.com/mpyw/suve/internal/usecase/param" @@ -38,28 +36,30 @@ func TestCommand_Validation(t *testing.T) { }) } -//nolint:lll // mock struct fields match AWS SDK interface signatures +// mockClient implements provider.ParameterTagger for testing. type mockClient struct { - addTagsFunc func(ctx context.Context, params *paramapi.AddTagsToResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) - removeTagsFunc func(ctx context.Context, params *paramapi.RemoveTagsFromResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) + addTagsFunc func(ctx context.Context, name string, tags map[string]string) error + removeTagsFunc func(ctx context.Context, name string, keys []string) error } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockClient) AddTagsToResource(ctx context.Context, params *paramapi.AddTagsToResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) { +func (m *mockClient) GetTags(_ context.Context, _ string) (map[string]string, error) { + return nil, nil //nolint:nilnil // mock implementation +} + +func (m *mockClient) AddTags(ctx context.Context, name string, tags map[string]string) error { if m.addTagsFunc != nil { - return m.addTagsFunc(ctx, params, optFns...) + return m.addTagsFunc(ctx, name, tags) } - return ¶mapi.AddTagsToResourceOutput{}, nil + return nil } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockClient) RemoveTagsFromResource(ctx context.Context, params *paramapi.RemoveTagsFromResourceInput, optFns ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) { +func (m *mockClient) RemoveTags(ctx context.Context, name string, keys []string) error { if m.removeTagsFunc != nil { - return m.removeTagsFunc(ctx, params, optFns...) + return m.removeTagsFunc(ctx, name, keys) } - return ¶mapi.RemoveTagsFromResourceOutput{}, nil + return nil } func TestRun(t *testing.T) { @@ -79,13 +79,11 @@ func TestRun(t *testing.T) { Keys: []string{"env"}, }, mock: &mockClient{ - //nolint:lll // inline mock function in test table - removeTagsFunc: func(_ context.Context, params *paramapi.RemoveTagsFromResourceInput, _ ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) { - assert.Equal(t, "/app/param", lo.FromPtr(params.ResourceId)) - assert.Equal(t, paramapi.ResourceTypeForTaggingParameter, params.ResourceType) - assert.Equal(t, []string{"env"}, params.TagKeys) + removeTagsFunc: func(_ context.Context, name string, keys []string) error { + assert.Equal(t, "/app/param", name) + assert.Equal(t, []string{"env"}, keys) - return ¶mapi.RemoveTagsFromResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -101,11 +99,10 @@ func TestRun(t *testing.T) { Keys: []string{"env", "team"}, }, mock: &mockClient{ - //nolint:lll // inline mock function in test table - removeTagsFunc: func(_ context.Context, params *paramapi.RemoveTagsFromResourceInput, _ ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) { - assert.Len(t, params.TagKeys, 2) + removeTagsFunc: func(_ context.Context, _ string, keys []string) error { + assert.Len(t, keys, 2) - return ¶mapi.RemoveTagsFromResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -120,9 +117,8 @@ func TestRun(t *testing.T) { Keys: []string{"env"}, }, mock: &mockClient{ - //nolint:lll // inline mock function in test table - removeTagsFunc: func(_ context.Context, _ *paramapi.RemoveTagsFromResourceInput, _ ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) { - return nil, fmt.Errorf("AWS error") + removeTagsFunc: func(_ context.Context, _ string, _ []string) error { + return fmt.Errorf("AWS error") }, }, wantErr: "failed to remove tags", diff --git a/internal/cli/commands/secret/tag/tag.go b/internal/cli/commands/secret/tag/tag.go index d9c91eb5..56b99622 100644 --- a/internal/cli/commands/secret/tag/tag.go +++ b/internal/cli/commands/secret/tag/tag.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli/v3" "github.com/mpyw/suve/internal/cli/output" - "github.com/mpyw/suve/internal/infra" + awssecret "github.com/mpyw/suve/internal/provider/aws/secret" "github.com/mpyw/suve/internal/usecase/secret" ) @@ -57,13 +57,13 @@ func action(ctx context.Context, cmd *cli.Command) error { return err } - client, err := infra.NewSecretClient(ctx) + adapter, err := awssecret.NewAdapter(ctx) if err != nil { return fmt.Errorf("failed to initialize AWS client: %w", err) } r := &Runner{ - UseCase: &secret.TagUseCase{Client: client}, + UseCase: &secret.TagUseCase{Client: adapter}, Stdout: cmd.Root().Writer, } diff --git a/internal/cli/commands/secret/tag/tag_test.go b/internal/cli/commands/secret/tag/tag_test.go index 4a3e1da2..3079e3d6 100644 --- a/internal/cli/commands/secret/tag/tag_test.go +++ b/internal/cli/commands/secret/tag/tag_test.go @@ -6,11 +6,9 @@ import ( "fmt" "testing" - "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mpyw/suve/internal/api/secretapi" appcli "github.com/mpyw/suve/internal/cli/commands" "github.com/mpyw/suve/internal/cli/commands/secret/tag" "github.com/mpyw/suve/internal/usecase/secret" @@ -56,42 +54,30 @@ func TestCommand_Validation(t *testing.T) { }) } +// mockClient implements provider.SecretTagger for testing. type mockClient struct { - //nolint:lll // mock function signature - describeSecretFunc func(ctx context.Context, params *secretapi.DescribeSecretInput, optFns ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) - //nolint:lll // mock function signature - tagResourceFunc func(ctx context.Context, params *secretapi.TagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) - //nolint:lll // mock function signature - untagResourceFunc func(ctx context.Context, params *secretapi.UntagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) + addTagsFunc func(ctx context.Context, name string, tags map[string]string) error + removeTagsFunc func(ctx context.Context, name string, keys []string) error } -//nolint:lll // mock function signature -func (m *mockClient) DescribeSecret(ctx context.Context, params *secretapi.DescribeSecretInput, optFns ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) { - if m.describeSecretFunc != nil { - return m.describeSecretFunc(ctx, params, optFns...) - } - - return &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, nil +func (m *mockClient) GetTags(_ context.Context, _ string) (map[string]string, error) { + return nil, nil //nolint:nilnil // mock implementation } -//nolint:lll // mock function signature -func (m *mockClient) TagResource(ctx context.Context, params *secretapi.TagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) { - if m.tagResourceFunc != nil { - return m.tagResourceFunc(ctx, params, optFns...) +func (m *mockClient) AddTags(ctx context.Context, name string, tags map[string]string) error { + if m.addTagsFunc != nil { + return m.addTagsFunc(ctx, name, tags) } - return &secretapi.TagResourceOutput{}, nil + return nil } -//nolint:lll // mock function signature -func (m *mockClient) UntagResource(ctx context.Context, params *secretapi.UntagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) { - if m.untagResourceFunc != nil { - return m.untagResourceFunc(ctx, params, optFns...) +func (m *mockClient) RemoveTags(ctx context.Context, name string, keys []string) error { + if m.removeTagsFunc != nil { + return m.removeTagsFunc(ctx, name, keys) } - return &secretapi.UntagResourceOutput{}, nil + return nil } func TestRun(t *testing.T) { @@ -111,12 +97,11 @@ func TestRun(t *testing.T) { Tags: map[string]string{"env": "prod"}, }, mock: &mockClient{ - //nolint:lll // mock function signature - tagResourceFunc: func(_ context.Context, params *secretapi.TagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) { - assert.Contains(t, lo.FromPtr(params.SecretId), "arn:aws:secretsmanager") - assert.Len(t, params.Tags, 1) + addTagsFunc: func(_ context.Context, name string, tags map[string]string) error { + assert.Equal(t, "my-secret", name) + assert.Len(t, tags, 1) - return &secretapi.TagResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -132,11 +117,10 @@ func TestRun(t *testing.T) { Tags: map[string]string{"env": "prod", "team": "backend"}, }, mock: &mockClient{ - //nolint:lll // mock function signature - tagResourceFunc: func(_ context.Context, params *secretapi.TagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) { - assert.Len(t, params.Tags, 2) + addTagsFunc: func(_ context.Context, _ string, tags map[string]string) error { + assert.Len(t, tags, 2) - return &secretapi.TagResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -145,28 +129,14 @@ func TestRun(t *testing.T) { }, }, { - name: "describe secret error", - opts: tag.Options{ - Name: "my-secret", - Tags: map[string]string{"env": "prod"}, - }, - mock: &mockClient{ - //nolint:lll // mock function signature - describeSecretFunc: func(_ context.Context, _ *secretapi.DescribeSecretInput, _ ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) { - return nil, fmt.Errorf("AWS error") - }, - }, - wantErr: "failed to describe secret", - }, - { - name: "tag resource error", + name: "add tags error", opts: tag.Options{ Name: "my-secret", Tags: map[string]string{"env": "prod"}, }, mock: &mockClient{ - tagResourceFunc: func(_ context.Context, _ *secretapi.TagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) { - return nil, fmt.Errorf("AWS error") + addTagsFunc: func(_ context.Context, _ string, _ map[string]string) error { + return fmt.Errorf("AWS error") }, }, wantErr: "failed to add tags", diff --git a/internal/cli/commands/secret/untag/untag.go b/internal/cli/commands/secret/untag/untag.go index 35016938..c6a1f830 100644 --- a/internal/cli/commands/secret/untag/untag.go +++ b/internal/cli/commands/secret/untag/untag.go @@ -9,7 +9,7 @@ import ( "github.com/urfave/cli/v3" "github.com/mpyw/suve/internal/cli/output" - "github.com/mpyw/suve/internal/infra" + awssecret "github.com/mpyw/suve/internal/provider/aws/secret" "github.com/mpyw/suve/internal/usecase/secret" ) @@ -50,13 +50,13 @@ func action(ctx context.Context, cmd *cli.Command) error { name := cmd.Args().Get(0) keys := cmd.Args().Slice()[1:] - client, err := infra.NewSecretClient(ctx) + adapter, err := awssecret.NewAdapter(ctx) if err != nil { return fmt.Errorf("failed to initialize AWS client: %w", err) } r := &Runner{ - UseCase: &secret.TagUseCase{Client: client}, + UseCase: &secret.TagUseCase{Client: adapter}, Stdout: cmd.Root().Writer, } diff --git a/internal/cli/commands/secret/untag/untag_test.go b/internal/cli/commands/secret/untag/untag_test.go index 1a213c13..09871d9b 100644 --- a/internal/cli/commands/secret/untag/untag_test.go +++ b/internal/cli/commands/secret/untag/untag_test.go @@ -6,11 +6,9 @@ import ( "fmt" "testing" - "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mpyw/suve/internal/api/secretapi" appcli "github.com/mpyw/suve/internal/cli/commands" "github.com/mpyw/suve/internal/cli/commands/secret/untag" "github.com/mpyw/suve/internal/usecase/secret" @@ -38,42 +36,30 @@ func TestCommand_Validation(t *testing.T) { }) } +// mockClient implements provider.SecretTagger for testing. type mockClient struct { - //nolint:lll // mock function signature - describeSecretFunc func(ctx context.Context, params *secretapi.DescribeSecretInput, optFns ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) - //nolint:lll // mock function signature - tagResourceFunc func(ctx context.Context, params *secretapi.TagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) - //nolint:lll // mock function signature - untagResourceFunc func(ctx context.Context, params *secretapi.UntagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) + addTagsFunc func(ctx context.Context, name string, tags map[string]string) error + removeTagsFunc func(ctx context.Context, name string, keys []string) error } -//nolint:lll // mock function signature -func (m *mockClient) DescribeSecret(ctx context.Context, params *secretapi.DescribeSecretInput, optFns ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) { - if m.describeSecretFunc != nil { - return m.describeSecretFunc(ctx, params, optFns...) - } - - return &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, nil +func (m *mockClient) GetTags(_ context.Context, _ string) (map[string]string, error) { + return nil, nil //nolint:nilnil // mock implementation } -//nolint:lll // mock function signature -func (m *mockClient) TagResource(ctx context.Context, params *secretapi.TagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) { - if m.tagResourceFunc != nil { - return m.tagResourceFunc(ctx, params, optFns...) +func (m *mockClient) AddTags(ctx context.Context, name string, tags map[string]string) error { + if m.addTagsFunc != nil { + return m.addTagsFunc(ctx, name, tags) } - return &secretapi.TagResourceOutput{}, nil + return nil } -//nolint:lll // mock function signature -func (m *mockClient) UntagResource(ctx context.Context, params *secretapi.UntagResourceInput, optFns ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) { - if m.untagResourceFunc != nil { - return m.untagResourceFunc(ctx, params, optFns...) +func (m *mockClient) RemoveTags(ctx context.Context, name string, keys []string) error { + if m.removeTagsFunc != nil { + return m.removeTagsFunc(ctx, name, keys) } - return &secretapi.UntagResourceOutput{}, nil + return nil } func TestRun(t *testing.T) { @@ -93,12 +79,11 @@ func TestRun(t *testing.T) { Keys: []string{"env"}, }, mock: &mockClient{ - //nolint:lll // mock function signature - untagResourceFunc: func(_ context.Context, params *secretapi.UntagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) { - assert.Contains(t, lo.FromPtr(params.SecretId), "arn:aws:secretsmanager") - assert.Equal(t, []string{"env"}, params.TagKeys) + removeTagsFunc: func(_ context.Context, name string, keys []string) error { + assert.Equal(t, "my-secret", name) + assert.Equal(t, []string{"env"}, keys) - return &secretapi.UntagResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -114,11 +99,10 @@ func TestRun(t *testing.T) { Keys: []string{"env", "team"}, }, mock: &mockClient{ - //nolint:lll // mock function signature - untagResourceFunc: func(_ context.Context, params *secretapi.UntagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) { - assert.Len(t, params.TagKeys, 2) + removeTagsFunc: func(_ context.Context, _ string, keys []string) error { + assert.Len(t, keys, 2) - return &secretapi.UntagResourceOutput{}, nil + return nil }, }, check: func(t *testing.T, output string) { @@ -127,29 +111,14 @@ func TestRun(t *testing.T) { }, }, { - name: "describe secret error", - opts: untag.Options{ - Name: "my-secret", - Keys: []string{"env"}, - }, - mock: &mockClient{ - //nolint:lll // mock function signature - describeSecretFunc: func(_ context.Context, _ *secretapi.DescribeSecretInput, _ ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) { - return nil, fmt.Errorf("AWS error") - }, - }, - wantErr: "failed to describe secret", - }, - { - name: "untag resource error", + name: "remove tags error", opts: untag.Options{ Name: "my-secret", Keys: []string{"env"}, }, mock: &mockClient{ - //nolint:lll // mock function signature - untagResourceFunc: func(_ context.Context, _ *secretapi.UntagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) { - return nil, fmt.Errorf("AWS error") + removeTagsFunc: func(_ context.Context, _ string, _ []string) error { + return fmt.Errorf("AWS error") }, }, wantErr: "failed to remove tags", diff --git a/internal/gui/param.go b/internal/gui/param.go index 6b7ca081..2913dcc9 100644 --- a/internal/gui/param.go +++ b/internal/gui/param.go @@ -6,6 +6,7 @@ import ( "errors" "github.com/mpyw/suve/internal/api/paramapi" + awsparam "github.com/mpyw/suve/internal/provider/aws/param" "github.com/mpyw/suve/internal/usecase/param" "github.com/mpyw/suve/internal/version/paramversion" ) @@ -293,12 +294,12 @@ func (a *App) ParamDelete(name string) (*ParamDeleteResult, error) { // ParamAddTag adds or updates a tag on a parameter. func (a *App) ParamAddTag(name, key, value string) error { - client, err := a.getParamClient() + adapter, err := awsparam.NewAdapter(a.ctx) if err != nil { return err } - uc := ¶m.TagUseCase{Client: client} + uc := ¶m.TagUseCase{Client: adapter} return uc.Execute(a.ctx, param.TagInput{ Name: name, @@ -308,12 +309,12 @@ func (a *App) ParamAddTag(name, key, value string) error { // ParamRemoveTag removes a tag from a parameter. func (a *App) ParamRemoveTag(name, key string) error { - client, err := a.getParamClient() + adapter, err := awsparam.NewAdapter(a.ctx) if err != nil { return err } - uc := ¶m.TagUseCase{Client: client} + uc := ¶m.TagUseCase{Client: adapter} return uc.Execute(a.ctx, param.TagInput{ Name: name, diff --git a/internal/gui/secret.go b/internal/gui/secret.go index 810ad851..1a078e3c 100644 --- a/internal/gui/secret.go +++ b/internal/gui/secret.go @@ -3,6 +3,7 @@ package gui import ( + awssecret "github.com/mpyw/suve/internal/provider/aws/secret" "github.com/mpyw/suve/internal/usecase/secret" "github.com/mpyw/suve/internal/version/secretversion" ) @@ -283,12 +284,12 @@ func (a *App) SecretDelete(name string, force bool) (*SecretDeleteResult, error) // SecretAddTag adds or updates a tag on a secret. func (a *App) SecretAddTag(name, key, value string) error { - client, err := a.getSecretClient() + adapter, err := awssecret.NewAdapter(a.ctx) if err != nil { return err } - uc := &secret.TagUseCase{Client: client} + uc := &secret.TagUseCase{Client: adapter} return uc.Execute(a.ctx, secret.TagInput{ Name: name, @@ -298,12 +299,12 @@ func (a *App) SecretAddTag(name, key, value string) error { // SecretRemoveTag removes a tag from a secret. func (a *App) SecretRemoveTag(name, key string) error { - client, err := a.getSecretClient() + adapter, err := awssecret.NewAdapter(a.ctx) if err != nil { return err } - uc := &secret.TagUseCase{Client: client} + uc := &secret.TagUseCase{Client: adapter} return uc.Execute(a.ctx, secret.TagInput{ Name: name, diff --git a/internal/provider/aws/param/adapter.go b/internal/provider/aws/param/adapter.go index 8c57c2c2..ce582e22 100644 --- a/internal/provider/aws/param/adapter.go +++ b/internal/provider/aws/param/adapter.go @@ -6,6 +6,8 @@ import ( "fmt" "strconv" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/samber/lo" "github.com/mpyw/suve/internal/api/paramapi" @@ -30,7 +32,17 @@ type Adapter struct { client Client } -// New creates a new AWS SSM adapter. +// NewAdapter creates a new AWS SSM adapter using the default AWS configuration. +func NewAdapter(ctx context.Context) (*Adapter, error) { + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + return &Adapter{client: ssm.NewFromConfig(cfg)}, nil +} + +// New creates a new AWS SSM adapter from an existing client. func New(client Client) *Adapter { return &Adapter{client: client} } diff --git a/internal/provider/aws/secret/adapter.go b/internal/provider/aws/secret/adapter.go index 895b53c3..ab8b3f5e 100644 --- a/internal/provider/aws/secret/adapter.go +++ b/internal/provider/aws/secret/adapter.go @@ -5,6 +5,8 @@ import ( "context" "fmt" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" "github.com/samber/lo" "github.com/mpyw/suve/internal/api/secretapi" @@ -31,7 +33,17 @@ type Adapter struct { client Client } -// New creates a new AWS Secrets Manager adapter. +// NewAdapter creates a new AWS Secrets Manager adapter using the default AWS configuration. +func NewAdapter(ctx context.Context) (*Adapter, error) { + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + return &Adapter{client: secretsmanager.NewFromConfig(cfg)}, nil +} + +// New creates a new AWS Secrets Manager adapter from an existing client. func New(client Client) *Adapter { return &Adapter{client: client} } diff --git a/internal/usecase/param/tag.go b/internal/usecase/param/tag.go index 179c4e61..ac760076 100644 --- a/internal/usecase/param/tag.go +++ b/internal/usecase/param/tag.go @@ -4,15 +4,13 @@ import ( "context" "fmt" - "github.com/samber/lo" - - "github.com/mpyw/suve/internal/api/paramapi" + "github.com/mpyw/suve/internal/provider" ) // TagClient is the interface for the tag use case. +// It uses the provider-agnostic ParameterTagger interface. type TagClient interface { - paramapi.AddTagsToResourceAPI - paramapi.RemoveTagsFromResourceAPI + provider.ParameterTagger } // TagInput holds input for the tag use case. @@ -31,31 +29,14 @@ type TagUseCase struct { func (u *TagUseCase) Execute(ctx context.Context, input TagInput) error { // Add tags if len(input.Add) > 0 { - tags := lo.MapToSlice(input.Add, func(k, v string) paramapi.Tag { - return paramapi.Tag{ - Key: lo.ToPtr(k), - Value: lo.ToPtr(v), - } - }) - - _, err := u.Client.AddTagsToResource(ctx, ¶mapi.AddTagsToResourceInput{ - ResourceId: lo.ToPtr(input.Name), - ResourceType: paramapi.ResourceTypeForTaggingParameter, - Tags: tags, - }) - if err != nil { + if err := u.Client.AddTags(ctx, input.Name, input.Add); err != nil { return fmt.Errorf("failed to add tags: %w", err) } } // Remove tags if len(input.Remove) > 0 { - _, err := u.Client.RemoveTagsFromResource(ctx, ¶mapi.RemoveTagsFromResourceInput{ - ResourceId: lo.ToPtr(input.Name), - ResourceType: paramapi.ResourceTypeForTaggingParameter, - TagKeys: input.Remove, - }) - if err != nil { + if err := u.Client.RemoveTags(ctx, input.Name, input.Remove); err != nil { return fmt.Errorf("failed to remove tags: %w", err) } } diff --git a/internal/usecase/param/tag_test.go b/internal/usecase/param/tag_test.go index 49bf2814..d22625f7 100644 --- a/internal/usecase/param/tag_test.go +++ b/internal/usecase/param/tag_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mpyw/suve/internal/api/paramapi" "github.com/mpyw/suve/internal/usecase/param" ) @@ -16,22 +15,16 @@ type mockTagClient struct { removeTagsErr error } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockTagClient) AddTagsToResource(_ context.Context, _ *paramapi.AddTagsToResourceInput, _ ...func(*paramapi.Options)) (*paramapi.AddTagsToResourceOutput, error) { - if m.addTagsErr != nil { - return nil, m.addTagsErr - } - - return ¶mapi.AddTagsToResourceOutput{}, nil +func (m *mockTagClient) GetTags(_ context.Context, _ string) (map[string]string, error) { + return nil, nil //nolint:nilnil // mock implementation } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockTagClient) RemoveTagsFromResource(_ context.Context, _ *paramapi.RemoveTagsFromResourceInput, _ ...func(*paramapi.Options)) (*paramapi.RemoveTagsFromResourceOutput, error) { - if m.removeTagsErr != nil { - return nil, m.removeTagsErr - } +func (m *mockTagClient) AddTags(_ context.Context, _ string, _ map[string]string) error { + return m.addTagsErr +} - return ¶mapi.RemoveTagsFromResourceOutput{}, nil +func (m *mockTagClient) RemoveTags(_ context.Context, _ string, _ []string) error { + return m.removeTagsErr } func TestTagUseCase_Execute_AddTags(t *testing.T) { diff --git a/internal/usecase/secret/tag.go b/internal/usecase/secret/tag.go index 4f2e63ab..bed24446 100644 --- a/internal/usecase/secret/tag.go +++ b/internal/usecase/secret/tag.go @@ -4,16 +4,12 @@ import ( "context" "fmt" - "github.com/samber/lo" - - "github.com/mpyw/suve/internal/api/secretapi" + "github.com/mpyw/suve/internal/provider" ) // TagClient is the interface for the tag use case. type TagClient interface { - secretapi.DescribeSecretAPI - secretapi.TagResourceAPI - secretapi.UntagResourceAPI + provider.SecretTagger } // TagInput holds input for the tag use case. @@ -30,42 +26,16 @@ type TagUseCase struct { // Execute runs the tag use case. func (u *TagUseCase) Execute(ctx context.Context, input TagInput) error { - // Get ARN first (required for tagging) - desc, err := u.Client.DescribeSecret(ctx, &secretapi.DescribeSecretInput{ - SecretId: lo.ToPtr(input.Name), - }) - if err != nil { - return fmt.Errorf("failed to describe secret: %w", err) - } - - arn := lo.FromPtr(desc.ARN) - // Add tags if len(input.Add) > 0 { - tags := make([]secretapi.Tag, 0, len(input.Add)) - for k, v := range input.Add { - tags = append(tags, secretapi.Tag{ - Key: lo.ToPtr(k), - Value: lo.ToPtr(v), - }) - } - - _, err := u.Client.TagResource(ctx, &secretapi.TagResourceInput{ - SecretId: lo.ToPtr(arn), - Tags: tags, - }) - if err != nil { + if err := u.Client.AddTags(ctx, input.Name, input.Add); err != nil { return fmt.Errorf("failed to add tags: %w", err) } } // Remove tags if len(input.Remove) > 0 { - _, err := u.Client.UntagResource(ctx, &secretapi.UntagResourceInput{ - SecretId: lo.ToPtr(arn), - TagKeys: input.Remove, - }) - if err != nil { + if err := u.Client.RemoveTags(ctx, input.Name, input.Remove); err != nil { return fmt.Errorf("failed to remove tags: %w", err) } } diff --git a/internal/usecase/secret/tag_test.go b/internal/usecase/secret/tag_test.go index b785aa3d..b93679f1 100644 --- a/internal/usecase/secret/tag_test.go +++ b/internal/usecase/secret/tag_test.go @@ -5,56 +5,33 @@ import ( "errors" "testing" - "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/mpyw/suve/internal/api/secretapi" "github.com/mpyw/suve/internal/usecase/secret" ) type mockTagClient struct { - describeResult *secretapi.DescribeSecretOutput - describeErr error - tagErr error - untagErr error + addTagsErr error + removeTagsErr error } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockTagClient) DescribeSecret(_ context.Context, _ *secretapi.DescribeSecretInput, _ ...func(*secretapi.Options)) (*secretapi.DescribeSecretOutput, error) { - if m.describeErr != nil { - return nil, m.describeErr - } - - return m.describeResult, nil +func (m *mockTagClient) GetTags(_ context.Context, _ string) (map[string]string, error) { + return nil, nil //nolint:nilnil // mock implementation } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockTagClient) TagResource(_ context.Context, _ *secretapi.TagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.TagResourceOutput, error) { - if m.tagErr != nil { - return nil, m.tagErr - } - - return &secretapi.TagResourceOutput{}, nil +func (m *mockTagClient) AddTags(_ context.Context, _ string, _ map[string]string) error { + return m.addTagsErr } -//nolint:lll // mock function signature must match AWS SDK interface -func (m *mockTagClient) UntagResource(_ context.Context, _ *secretapi.UntagResourceInput, _ ...func(*secretapi.Options)) (*secretapi.UntagResourceOutput, error) { - if m.untagErr != nil { - return nil, m.untagErr - } - - return &secretapi.UntagResourceOutput{}, nil +func (m *mockTagClient) RemoveTags(_ context.Context, _ string, _ []string) error { + return m.removeTagsErr } func TestTagUseCase_Execute_AddTags(t *testing.T) { t.Parallel() - client := &mockTagClient{ - describeResult: &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, - } + client := &mockTagClient{} uc := &secret.TagUseCase{Client: client} err := uc.Execute(t.Context(), secret.TagInput{ @@ -67,11 +44,7 @@ func TestTagUseCase_Execute_AddTags(t *testing.T) { func TestTagUseCase_Execute_RemoveTags(t *testing.T) { t.Parallel() - client := &mockTagClient{ - describeResult: &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, - } + client := &mockTagClient{} uc := &secret.TagUseCase{Client: client} err := uc.Execute(t.Context(), secret.TagInput{ @@ -84,11 +57,7 @@ func TestTagUseCase_Execute_RemoveTags(t *testing.T) { func TestTagUseCase_Execute_AddAndRemoveTags(t *testing.T) { t.Parallel() - client := &mockTagClient{ - describeResult: &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, - } + client := &mockTagClient{} uc := &secret.TagUseCase{Client: client} err := uc.Execute(t.Context(), secret.TagInput{ @@ -102,11 +71,7 @@ func TestTagUseCase_Execute_AddAndRemoveTags(t *testing.T) { func TestTagUseCase_Execute_NoTags(t *testing.T) { t.Parallel() - client := &mockTagClient{ - describeResult: &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, - } + client := &mockTagClient{} uc := &secret.TagUseCase{Client: client} err := uc.Execute(t.Context(), secret.TagInput{ @@ -115,30 +80,11 @@ func TestTagUseCase_Execute_NoTags(t *testing.T) { require.NoError(t, err) } -func TestTagUseCase_Execute_DescribeError(t *testing.T) { - t.Parallel() - - client := &mockTagClient{ - describeErr: errors.New("describe failed"), - } - uc := &secret.TagUseCase{Client: client} - - err := uc.Execute(t.Context(), secret.TagInput{ - Name: "my-secret", - Add: map[string]string{"env": "prod"}, - }) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to describe secret") -} - func TestTagUseCase_Execute_AddTagsError(t *testing.T) { t.Parallel() client := &mockTagClient{ - describeResult: &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, - tagErr: errors.New("tag failed"), + addTagsErr: errors.New("add tags failed"), } uc := &secret.TagUseCase{Client: client} @@ -154,10 +100,7 @@ func TestTagUseCase_Execute_RemoveTagsError(t *testing.T) { t.Parallel() client := &mockTagClient{ - describeResult: &secretapi.DescribeSecretOutput{ - ARN: lo.ToPtr("arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret"), - }, - untagErr: errors.New("untag failed"), + removeTagsErr: errors.New("remove tags failed"), } uc := &secret.TagUseCase{Client: client}