diff --git a/internal/auth/scopes.go b/internal/auth/scopes.go index f6f6d55c2..a9d31dcfb 100644 --- a/internal/auth/scopes.go +++ b/internal/auth/scopes.go @@ -11,6 +11,7 @@ var RequiredScopes = []string{ "create:users", "delete:users", "read:users", "update:users", "read:branding", "update:branding", "read:email_templates", "update:email_templates", + "read:email_provider", "read:connections", "update:connections", "read:client_keys", "read:logs", "read:tenant_settings", "read:custom_domains", "create:custom_domains", "update:custom_domains", "delete:custom_domains", diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index 537d840ee..6ad73bdcf 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -17,6 +17,7 @@ type API struct { Connection ConnectionAPI CustomDomain CustomDomainAPI EmailTemplate EmailTemplateAPI + EmailProvider EmailProviderAPI Log LogAPI LogStream LogStreamAPI Organization OrganizationAPI @@ -42,6 +43,7 @@ func NewAPI(m *management.Management) *API { Connection: m.Connection, CustomDomain: m.CustomDomain, EmailTemplate: m.EmailTemplate, + EmailProvider: m.EmailProvider, Log: m.Log, LogStream: m.LogStream, Organization: m.Organization, diff --git a/internal/auth0/email_provider.go b/internal/auth0/email_provider.go new file mode 100644 index 000000000..ee4244989 --- /dev/null +++ b/internal/auth0/email_provider.go @@ -0,0 +1,13 @@ +package auth0 + +import ( + "context" + + "github.com/auth0/go-auth0/management" +) + +type EmailProviderAPI interface { + // Read email provider details. + // See: https://auth0.com/docs/api/management/v2#!/Emails/get_provider + Read(ctx context.Context, opts ...management.RequestOption) (ep *management.EmailProvider, err error) +} diff --git a/internal/auth0/mock/email_provider_mock.go b/internal/auth0/mock/email_provider_mock.go new file mode 100644 index 000000000..c153e1f79 --- /dev/null +++ b/internal/auth0/mock/email_provider_mock.go @@ -0,0 +1,56 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: action.go + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + management "github.com/auth0/go-auth0/management" + gomock "github.com/golang/mock/gomock" +) + +// MockEmailProviderAPI is a mock of EmailProviderAPI interface. +type MockEmailProviderAPI struct { + ctrl *gomock.Controller + recorder *MockEmailProviderAPIMockRecorder +} + +// MockEmailProviderAPIMockRecorder is the mock recorder for MockEmailProviderAPI. +type MockEmailProviderAPIMockRecorder struct { + mock *MockEmailProviderAPI +} + +// NewMockEmailProviderAPI creates a new mock instance. +func NewMockEmailProviderAPI(ctrl *gomock.Controller) *MockEmailProviderAPI { + mock := &MockEmailProviderAPI{ctrl: ctrl} + mock.recorder = &MockEmailProviderAPIMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEmailProviderAPI) EXPECT() *MockEmailProviderAPIMockRecorder { + return m.recorder +} + +// Read mocks base method. +func (m *MockEmailProviderAPI) Read(ctx context.Context, opts ...management.RequestOption) (*management.EmailProvider, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Read", varargs...) + ret0, _ := ret[0].(*management.EmailProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read. +func (mr *MockEmailProviderAPIMockRecorder) Read(ctx interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockEmailProviderAPI)(nil).Read), varargs...) +} \ No newline at end of file diff --git a/internal/cli/terraform.go b/internal/cli/terraform.go index 9252d867b..27157440a 100644 --- a/internal/cli/terraform.go +++ b/internal/cli/terraform.go @@ -73,7 +73,7 @@ func (i *terraformInputs) parseResourceFetchers(api *auth0.API) ([]resourceDataF case "auth0_custom_domain": fetchers = append(fetchers, &customDomainResourceFetcher{api}) case "auth0_email_provider": - fetchers = append(fetchers, &emailProviderResourceFetcher{}) + fetchers = append(fetchers, &emailProviderResourceFetcher{api}) case "auth0_guardian": fetchers = append(fetchers, &guardianResourceFetcher{}) case "auth0_log_stream": diff --git a/internal/cli/terraform_fetcher.go b/internal/cli/terraform_fetcher.go index f8a030bae..e85cad290 100644 --- a/internal/cli/terraform_fetcher.go +++ b/internal/cli/terraform_fetcher.go @@ -2,6 +2,7 @@ package cli import ( "context" + "net/http" "strings" "github.com/auth0/go-auth0/management" @@ -49,7 +50,9 @@ type ( api *auth0.API } - emailProviderResourceFetcher struct{} + emailProviderResourceFetcher struct { + api *auth0.API + } guardianResourceFetcher struct{} logStreamResourceFetcher struct { @@ -220,7 +223,15 @@ func (f *customDomainResourceFetcher) FetchData(ctx context.Context) (importData return data, nil } -func (f *emailProviderResourceFetcher) FetchData(_ context.Context) (importDataList, error) { +func (f *emailProviderResourceFetcher) FetchData(ctx context.Context) (importDataList, error) { + _, err := f.api.EmailProvider.Read(ctx) + if err != nil { + if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound { + return nil, nil + } + return nil, err + } + return []importDataItem{ { ResourceName: "auth0_email_provider.email_provider", diff --git a/internal/cli/terraform_fetcher_test.go b/internal/cli/terraform_fetcher_test.go index 3ecbdc1ce..ea70c825d 100644 --- a/internal/cli/terraform_fetcher_test.go +++ b/internal/cli/terraform_fetcher_test.go @@ -3,6 +3,7 @@ package cli import ( "context" "fmt" + "net/http" "strings" "testing" @@ -560,7 +561,19 @@ func TestGuardianResourceFetcher_FetchData(t *testing.T) { func TestEmailProviderResourceFetcher_FetchData(t *testing.T) { t.Run("it successfully generates email provider import data", func(t *testing.T) { - fetcher := emailProviderResourceFetcher{} + ctrl := gomock.NewController(t) + defer ctrl.Finish() + emailProviderAPI := mock.NewMockEmailProviderAPI(ctrl) + + emailProviderAPI.EXPECT(). + Read(gomock.Any(), gomock.Any()). + Return(nil, nil) + + fetcher := emailProviderResourceFetcher{ + api: &auth0.API{ + EmailProvider: emailProviderAPI, + }, + } data, err := fetcher.FetchData(context.Background()) assert.NoError(t, err) @@ -568,6 +581,47 @@ func TestEmailProviderResourceFetcher_FetchData(t *testing.T) { assert.Equal(t, data[0].ResourceName, "auth0_email_provider.email_provider") assert.Greater(t, len(data[0].ImportID), 0) }) + + t.Run("it does not generate email provider import data if email provider does not exist", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mErr := mockManagamentError{status: http.StatusNotFound} + + emailProviderAPI := mock.NewMockEmailProviderAPI(ctrl) + emailProviderAPI.EXPECT(). + Read(gomock.Any(), gomock.Any()). + Return(nil, mErr) + + fetcher := emailProviderResourceFetcher{ + api: &auth0.API{ + EmailProvider: emailProviderAPI, + }, + } + + data, err := fetcher.FetchData(context.Background()) + assert.NoError(t, err) + assert.Len(t, data, 0) + }) + + t.Run("it returns an error if api call fails", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + emailProviderAPI := mock.NewMockEmailProviderAPI(ctrl) + emailProviderAPI.EXPECT(). + Read(gomock.Any()). + Return(nil, fmt.Errorf("failed to read email provider")) + + fetcher := emailProviderResourceFetcher{ + api: &auth0.API{ + EmailProvider: emailProviderAPI, + }, + } + + _, err := fetcher.FetchData(context.Background()) + assert.EqualError(t, err, "failed to read email provider") + }) } func TestLogStreamResourceFetcher_FetchData(t *testing.T) {