diff --git a/services/describe/api/jobs.go b/services/describe/api/jobs.go index 31b24513d..32d6b85e4 100644 --- a/services/describe/api/jobs.go +++ b/services/describe/api/jobs.go @@ -136,11 +136,12 @@ type GetComplianceJobsHistoryResponse struct { type BenchmarkAuditHistoryItem struct { JobId uint `json:"job_id"` + WithIncidents bool `json:"with_incidents"` BenchmarkId string `json:"benchmark_id"` JobStatus ComplianceJobStatus `json:"job_status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` - IntegrationInfo IntegrationInfo `json:"integration_info"` + IntegrationInfo []IntegrationInfo `json:"integration_info"` NumberOfIntegrations int `json:"number_of_integrations"` } @@ -264,6 +265,7 @@ type ListComplianceJobsRequest struct { type BenchmarkAuditHistoryRequest struct { IntegrationInfo []IntegrationInfoFilter `json:"integration_info"` JobStatus []string `json:"job_status"` + WithIncidents *bool `json:"with_incidents"` Interval *string `json:"interval"` StartTime time.Time `json:"start_time"` EndTime *time.Time `json:"end_time"` diff --git a/services/describe/db/compliance_quick_run.go b/services/describe/db/compliance_quick_run.go index aa8e2daf0..7de99a3d7 100644 --- a/services/describe/db/compliance_quick_run.go +++ b/services/describe/db/compliance_quick_run.go @@ -2,8 +2,10 @@ package db import ( "errors" + "github.com/lib/pq" "github.com/opengovern/opencomply/services/describe/db/model" "gorm.io/gorm" + "time" ) func (db Database) CreateComplianceQuickRun(job *model.ComplianceQuickRun) (uint, error) { @@ -38,6 +40,36 @@ func (db Database) ListComplianceQuickRuns() ([]model.ComplianceQuickRun, error) return job, nil } +func (db Database) ListComplianceQuickRunsByFilters(integrationId []string, benchmarkId []string, status []string, + startTime, endTime *time.Time) ([]model.ComplianceQuickRun, error) { + var jobs []model.ComplianceQuickRun + tx := db.ORM.Model(&model.ComplianceQuickRun{}) + + if len(integrationId) > 0 { + tx = tx.Where("integration_ids && ?", pq.Array(integrationId)) + } + + if len(benchmarkId) > 0 { + tx = tx.Where("framework_id IN ?", benchmarkId) + } + if len(status) > 0 { + tx = tx.Where("status IN ?", status) + } + if startTime != nil { + tx = tx.Where("updated_at >= ?", *startTime) + } + if endTime != nil { + tx = tx.Where("updated_at <= ?", *endTime) + } + + tx = tx.Find(&jobs) + if tx.Error != nil { + return nil, tx.Error + } + + return jobs, nil +} + func (db Database) CleanupAllComplianceQuickRuns() error { tx := db.ORM.Where("1 = 1").Unscoped().Delete(&model.ComplianceQuickRun{}) if tx.Error != nil { diff --git a/services/describe/server.go b/services/describe/server.go index d962bafbb..e78d9bf4b 100644 --- a/services/describe/server.go +++ b/services/describe/server.go @@ -1198,6 +1198,19 @@ func (h HttpServer) RunBenchmarkById(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "invalid request") } + benchmark, err := h.Scheduler.complianceClient.GetBenchmark(&httpclient.Context{UserRole: apiAuth.AdminRole}, benchmarkID) + if err != nil { + return fmt.Errorf("error while getting benchmarks: %v", err) + } + if benchmark == nil { + return echo.NewHTTPError(http.StatusNotFound, "benchmark not found") + } + + validIntegrationTypes := make(map[string]bool) + for _, it := range benchmark.IntegrationTypes { + validIntegrationTypes[it] = true + } + var integrations []integrationapi.Integration for _, info := range request.IntegrationInfo { if info.IntegrationID != nil { @@ -1231,6 +1244,9 @@ func (h HttpServer) RunBenchmarkById(ctx echo.Context) error { var connectionInfo []api.IntegrationInfo var connectionIDs []string for _, c := range integrations { + if _, ok := validIntegrationTypes[c.IntegrationType.String()]; !ok { + return echo.NewHTTPError(http.StatusBadRequest, "invalid integration type for this framework") + } connectionInfo = append(connectionInfo, api.IntegrationInfo{ IntegrationID: c.IntegrationID, IntegrationType: string(c.IntegrationType), @@ -1240,11 +1256,6 @@ func (h HttpServer) RunBenchmarkById(ctx echo.Context) error { connectionIDs = append(connectionIDs, c.IntegrationID) } - benchmark, err := h.Scheduler.complianceClient.GetBenchmark(&httpclient.Context{UserRole: apiAuth.AdminRole}, benchmarkID) - if err != nil { - return fmt.Errorf("error while getting benchmarks: %v", err) - } - if benchmark == nil { return echo.NewHTTPError(http.StatusNotFound, "benchmark not found") } @@ -2004,30 +2015,64 @@ func (h HttpServer) BenchmarkAuditHistory(ctx echo.Context) error { startTime = &request.StartTime endTime = request.EndTime } - - jobs, err := h.DB.ListComplianceJobsByFilters(connectionIDs, []string{benchmarkID}, request.JobStatus, startTime, endTime) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - var items []api.BenchmarkAuditHistoryItem - for _, j := range jobs { - item := api.BenchmarkAuditHistoryItem{ - JobId: j.ID, - BenchmarkId: j.BenchmarkID, - JobStatus: j.Status.ToApi(), - CreatedAt: j.CreatedAt, - UpdatedAt: j.UpdatedAt, + + if request.WithIncidents == nil || *request.WithIncidents == true { + // With Incidents + jobs, err := h.DB.ListComplianceJobsByFilters(connectionIDs, []string{benchmarkID}, request.JobStatus, startTime, endTime) + if err != nil { + h.Scheduler.logger.Error("failed to get list of compliance jobs", zap.Error(err)) + return echo.NewHTTPError(http.StatusInternalServerError, "failed to get list of compliance jobs") } - if info, ok := connectionInfo[j.IntegrationID]; ok { - item.IntegrationInfo = info - item.NumberOfIntegrations = 1 + + for _, j := range jobs { + item := api.BenchmarkAuditHistoryItem{ + JobId: j.ID, + WithIncidents: true, + BenchmarkId: j.BenchmarkID, + JobStatus: j.Status.ToApi(), + CreatedAt: j.CreatedAt, + UpdatedAt: j.UpdatedAt, + } + if info, ok := connectionInfo[j.IntegrationID]; ok { + item.IntegrationInfo = []api.IntegrationInfo{info} + item.NumberOfIntegrations = 1 + } + + items = append(items, item) } + } + + if request.WithIncidents == nil || *request.WithIncidents == false { + // Without Incidents + jobs2, err := h.DB.ListComplianceQuickRunsByFilters(connectionIDs, []string{benchmarkID}, request.JobStatus, startTime, endTime) + if err != nil { + h.Scheduler.logger.Error("failed to get list of compliance jobs", zap.Error(err)) + return echo.NewHTTPError(http.StatusInternalServerError, "failed to get list of compliance jobs") + } + + var items2 []api.BenchmarkAuditHistoryItem + for _, j := range jobs2 { + item := api.BenchmarkAuditHistoryItem{ + JobId: j.ID, + WithIncidents: false, + BenchmarkId: j.FrameworkID, + JobStatus: api.ComplianceJobStatus(j.Status), + CreatedAt: j.CreatedAt, + UpdatedAt: j.UpdatedAt, + } + item.NumberOfIntegrations = len(j.IntegrationIDs) + for _, i := range j.IntegrationIDs { + if info, ok := connectionInfo[i]; ok { + item.IntegrationInfo = append(item.IntegrationInfo, info) + } + } - items = append(items, item) + items2 = append(items2, item) + } + items = append(items, items2...) } - totalCount := len(items) if request.SortBy != nil { switch strings.ToLower(*request.SortBy) { case "id": @@ -2060,6 +2105,8 @@ func (h HttpServer) BenchmarkAuditHistory(ctx echo.Context) error { return items[i].UpdatedAt.After(items[j].UpdatedAt) }) } + + totalCount := len(items) if request.PerPage != nil { if request.Cursor == nil { items = utils.Paginate(1, *request.PerPage, items) diff --git a/services/integration/integration-type/doppler-account/configs/credentials.go b/services/integration/integration-type/doppler-account/configs/credentials.go index 09e7bf339..826824a4b 100644 --- a/services/integration/integration-type/doppler-account/configs/credentials.go +++ b/services/integration/integration-type/doppler-account/configs/credentials.go @@ -1,5 +1,5 @@ package configs type IntegrationCredentials struct { -Token string `json:"token"` + Token string `json:"token"` } diff --git a/services/integration/integration-type/doppler-account/configs/resource_types_list.go b/services/integration/integration-type/doppler-account/configs/resource_types_list.go index 6dd4a8a12..18bf421c1 100755 --- a/services/integration/integration-type/doppler-account/configs/resource_types_list.go +++ b/services/integration/integration-type/doppler-account/configs/resource_types_list.go @@ -1,23 +1,37 @@ package configs var TablesToResourceTypes = map[string]string{ - + "doppler_config": "Doppler/Config", + "doppler_environment": "Doppler/Environment", + "doppler_group": "Doppler/Group", + "doppler_integration": "Doppler/Integration", + "doppler_project_member": "Doppler/Project/Member", + "doppler_project_role": "Doppler/Project/Role", + "doppler_project": "Doppler/Project", + "doppler_secret": "Doppler/Secret", + "doppler_service_account": "Doppler/ServiceAccount", + "doppler_service_account_token": "Doppler/ServiceAccount/Token", + "doppler_service_token": "Doppler/ServiceToken", + "doppler_trust_ip": "Doppler/TrustIP", + "doppler_workplace": "Doppler/WorkPlace", + "doppler_workplace_role": "Doppler/WorkPlace/Role", + "doppler_workplace_user": "Doppler/WorkPlace/User", } var ResourceTypesList = []string{ - "Doppler/Config", - "Doppler/Environment", - "Doppler/Group", - "Doppler/Integration", - "Doppler/Project/Member", - "Doppler/Project/Role", - "Doppler/Project", - "Doppler/Secret", - "Doppler/ServiceAccount", - "Doppler/ServiceAccount/Token", - "Doppler/ServiceToken", - "Doppler/TrustIP", - "Doppler/WorkPlace", - "Doppler/WorkPlace/Role", - "Doppler/WorkPlace/User", -} \ No newline at end of file + "Doppler/Config", + "Doppler/Environment", + "Doppler/Group", + "Doppler/Integration", + "Doppler/Project/Member", + "Doppler/Project/Role", + "Doppler/Project", + "Doppler/Secret", + "Doppler/ServiceAccount", + "Doppler/ServiceAccount/Token", + "Doppler/ServiceToken", + "Doppler/TrustIP", + "Doppler/WorkPlace", + "Doppler/WorkPlace/Role", + "Doppler/WorkPlace/User", +} diff --git a/services/integration/integration-type/doppler-account/discovery/doppler_integration_discovery.go b/services/integration/integration-type/doppler-account/discovery/doppler_integration_discovery.go index 2327c8681..8d43626c2 100644 --- a/services/integration/integration-type/doppler-account/discovery/doppler_integration_discovery.go +++ b/services/integration/integration-type/doppler-account/discovery/doppler_integration_discovery.go @@ -4,85 +4,59 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" - "time" - ) -type ConnectorResponse struct { - Connectors []Connector `json:"connectors"` - TotalCount float64 `json:"total_count"` +// Config represents the JSON input configuration +type Config struct { + Token string `json:"token"` } - - - -type Connector struct { - ID string `json:"id"` - Name string `json:"name"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - OrganizationID string `json:"organization_id"` - Description string `json:"description"` - URL string `json:"url"` - Excludes []string `json:"excludes"` - AuthType string `json:"auth_type"` - Oauth Oauth `json:"oauth"` - AuthStatus string `json:"auth_status"` - Active bool `json:"active"` - ContinueOnFailure bool `json:"continue_on_failure"` +// Workplace defines the information for doppler workplace. +type Workplace struct { + ID string `json:"id"` + Name string `json:"name"` + BillingEmail string `json:"billing_email"` + SecurityEmail string `json:"security_email"` } -type Oauth struct { - AuthorizeURL string `json:"authorize_url"` - TokenURL string `json:"token_url"` +type Response struct { + Workplace Workplace `json:"workplace"` } +// Discover retrieves Render customer info +func Discover(token string) (*Workplace, error) { + var response Response + url := "https://api.doppler.com/v3/workplace" -func DopplerIntegrationDiscovery(apiKey string) ([]Connector, error) { - if apiKey == "" { - return nil, errors.New("API key is required") - } - - // Endpoint to test access - url := "https://api.cohere.com/v1/connectors" + client := http.DefaultClient - // Create HTTP request req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, fmt.Errorf("error creating request: %v", err) + return nil, err } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - // Add Authorization header - req.Header.Add("Authorization", "Bearer "+apiKey) - - // Execute the request - client := &http.Client{} resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("error making request: %v", err) + return nil, fmt.Errorf("request execution failed: %w", err) } defer resp.Body.Close() - // Check for non-200 status codes - if resp.StatusCode != http.StatusOK { - body, _ := ioutil.ReadAll(resp.Body) - return nil, fmt.Errorf("API error: %s, status code: %d", string(body), resp.StatusCode) + if err = json.NewDecoder(resp.Body).Decode(&response); err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) } - // Parse the response to ensure it contains models data - var modelsResponse ConnectorResponse - err = json.NewDecoder(resp.Body).Decode(&modelsResponse) - if err != nil { - return nil, fmt.Errorf("error parsing response: %v", err) - } + return &response.Workplace, nil +} - // Validate that the token provides access to at least one model - if len(modelsResponse.Connectors) == 0 { - return nil, nil // Token valid but no accessible models +func DopplerIntegrationDiscovery(cfg Config) (*Workplace, error) { + // Check for the token + if cfg.Token == "" { + return nil, errors.New("token must be configured") } - return modelsResponse.Connectors, nil // Token valid and has access + return Discover(cfg.Token) } diff --git a/services/integration/integration-type/doppler-account/doppler_account.go b/services/integration/integration-type/doppler-account/doppler_account.go index 2dfd631bf..ee7f97642 100644 --- a/services/integration/integration-type/doppler-account/doppler_account.go +++ b/services/integration/integration-type/doppler-account/doppler_account.go @@ -3,11 +3,10 @@ package doppler_account import ( "encoding/json" "github.com/jackc/pgtype" - cohereaiDescriberLocal "github.com/opengovern/opencomply/services/integration/integration-type/cohereai-project/configs" + dopplerDescriberLocal "github.com/opengovern/opencomply/services/integration/integration-type/doppler-account/configs" + "github.com/opengovern/opencomply/services/integration/integration-type/doppler-account/discovery" + "github.com/opengovern/opencomply/services/integration/integration-type/doppler-account/healthcheck" "github.com/opengovern/opencomply/services/integration/integration-type/interfaces" - "github.com/opengovern/opencomply/services/integration/integration-type/cohereai-project/healthcheck" - "github.com/opengovern/opencomply/services/integration/integration-type/cohereai-project/discovery" - "github.com/opengovern/opencomply/services/integration/models" ) @@ -15,49 +14,47 @@ type DopplerAccountIntegration struct{} func (i *DopplerAccountIntegration) GetConfiguration() interfaces.IntegrationConfiguration { return interfaces.IntegrationConfiguration{ - NatsScheduledJobsTopic: cohereaiDescriberLocal.JobQueueTopic, - NatsManualJobsTopic: cohereaiDescriberLocal.JobQueueTopicManuals, - NatsStreamName: cohereaiDescriberLocal.StreamName, - NatsConsumerGroup: cohereaiDescriberLocal.ConsumerGroup, - NatsConsumerGroupManuals: cohereaiDescriberLocal.ConsumerGroupManuals, + NatsScheduledJobsTopic: dopplerDescriberLocal.JobQueueTopic, + NatsManualJobsTopic: dopplerDescriberLocal.JobQueueTopicManuals, + NatsStreamName: dopplerDescriberLocal.StreamName, + NatsConsumerGroup: dopplerDescriberLocal.ConsumerGroup, + NatsConsumerGroupManuals: dopplerDescriberLocal.ConsumerGroupManuals, SteampipePluginName: "doppler", UISpecFileName: "doppler-account.json", - DescriberDeploymentName: cohereaiDescriberLocal.DescriberDeploymentName, - DescriberRunCommand: cohereaiDescriberLocal.DescriberRunCommand, + DescriberDeploymentName: dopplerDescriberLocal.DescriberDeploymentName, + DescriberRunCommand: dopplerDescriberLocal.DescriberRunCommand, } } func (i *DopplerAccountIntegration) HealthCheck(jsonData []byte, providerId string, labels map[string]string, annotations map[string]string) (bool, error) { - var credentials cohereaiDescriberLocal.IntegrationCredentials + var credentials dopplerDescriberLocal.IntegrationCredentials err := json.Unmarshal(jsonData, &credentials) if err != nil { return false, err } - isHealthy, err := healthcheck.CohereAIIntegrationHealthcheck(credentials.APIKey) + isHealthy, err := healthcheck.DopplerIntegrationHealthcheck(healthcheck.Config{ + Token: credentials.Token, + }) return isHealthy, err } func (i *DopplerAccountIntegration) DiscoverIntegrations(jsonData []byte) ([]models.Integration, error) { - var credentials cohereaiDescriberLocal.IntegrationCredentials + var credentials dopplerDescriberLocal.IntegrationCredentials err := json.Unmarshal(jsonData, &credentials) if err != nil { return nil, err } var integrations []models.Integration - connectors, err1 := discovery.CohereAIIntegrationDiscovery(credentials.APIKey) - if err1 != nil { - return nil, err1 - } + workplace, err := discovery.DopplerIntegrationDiscovery(discovery.Config{ + Token: credentials.Token, + }) labels := map[string]string{ - "ClientName": credentials.ClientName, - - } - if(len(connectors) > 0){ - labels["OrganizationID"] = connectors[0].OrganizationID + "BillingEmail": workplace.BillingEmail, + "SecurityEmail": workplace.SecurityEmail, } labelsJsonData, err := json.Marshal(labels) if err != nil { @@ -68,28 +65,21 @@ func (i *DopplerAccountIntegration) DiscoverIntegrations(jsonData []byte) ([]mod if err != nil { return nil, err } - // for in esponse - for _, connector := range connectors { -integrations = append(integrations, models.Integration{ - ProviderID: connector.ID, - Name: connector.Name, + integrations = append(integrations, models.Integration{ + ProviderID: workplace.ID, + Name: workplace.Name, Labels: integrationLabelsJsonb, }) - } - - - return integrations, nil } func (i *DopplerAccountIntegration) GetResourceTypesByLabels(map[string]string) ([]string, error) { - return cohereaiDescriberLocal.ResourceTypesList, nil + return dopplerDescriberLocal.ResourceTypesList, nil } func (i *DopplerAccountIntegration) GetResourceTypeFromTableName(tableName string) string { - if v, ok := cohereaiDescriberLocal.TablesToResourceTypes[tableName]; ok { + if v, ok := dopplerDescriberLocal.TablesToResourceTypes[tableName]; ok { return v } - return "" } diff --git a/services/integration/integration-type/doppler-account/healthcheck/doppler_integration_healthcheck.go b/services/integration/integration-type/doppler-account/healthcheck/doppler_integration_healthcheck.go index a7c037e31..7e1b35fe3 100644 --- a/services/integration/integration-type/doppler-account/healthcheck/doppler_integration_healthcheck.go +++ b/services/integration/integration-type/doppler-account/healthcheck/doppler_integration_healthcheck.go @@ -4,66 +4,60 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" - ) -type ConnectorResponse struct { - Connectors []Connector `json:"connectors"` - TotalCount float64 `json:"total_count"` +// Config represents the JSON input configuration +type Config struct { + Token string `json:"token"` } - -type Connector struct { - ID string `json:"id"` - +type Response struct { + Success bool `json:"success"` } +// IsHealthy checks if the JWT has read access to all required resources +func IsHealthy(token string) error { + var response Response + url := "https://api.doppler.com/v3/workplace" -func DopplerIntegrationHealthcheck(apiKey string) (bool, error) { - if apiKey == "" { - return false, errors.New("API key is required") - } - - // Endpoint to test access - url := "https://api.cohere.com/v1/connectors" + client := http.DefaultClient - // Create HTTP request req, err := http.NewRequest("GET", url, nil) if err != nil { - return false, fmt.Errorf("error creating request: %v", err) + return err } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - // Add Authorization header - req.Header.Add("Authorization", "Bearer "+apiKey) - - // Execute the request - client := &http.Client{} resp, err := client.Do(req) if err != nil { - return false, fmt.Errorf("error making request: %v", err) + return fmt.Errorf("request execution failed: %w", err) } defer resp.Body.Close() - // Check for non-200 status codes - if resp.StatusCode != http.StatusOK { - body, _ := ioutil.ReadAll(resp.Body) - return false, fmt.Errorf("API error: %s, status code: %d", string(body), resp.StatusCode) + if err = json.NewDecoder(resp.Body).Decode(&response); err != nil { + return fmt.Errorf("failed to decode response: %w", err) } - // Parse the response to ensure it contains models data - var modelsResponse ConnectorResponse - err = json.NewDecoder(resp.Body).Decode(&modelsResponse) - if err != nil { - return false, fmt.Errorf("error parsing response: %v", err) + if !response.Success { + return fmt.Errorf("failed to fetch workplace") } - // Validate that the token provides access to at least one model - if len(modelsResponse.Connectors) == 0 { - return false, nil // Token valid but no accessible models + return nil +} + +func DopplerIntegrationHealthcheck(cfg Config) (bool, error) { + // Check for the token + if cfg.Token == "" { + return false, errors.New("api key must be configured") + } + + err := IsHealthy(cfg.Token) + if err != nil { + return false, err } - return true, nil // Token valid and has access + return true, nil } diff --git a/services/integration/integration-type/integrations.go b/services/integration/integration-type/integrations.go index 3c9903fdb..4873b0021 100644 --- a/services/integration/integration-type/integrations.go +++ b/services/integration/integration-type/integrations.go @@ -14,6 +14,8 @@ import ( cohereaiConfigs "github.com/opengovern/opencomply/services/integration/integration-type/cohereai-project/configs" "github.com/opengovern/opencomply/services/integration/integration-type/digitalocean-team" digitalOceanConfigs "github.com/opengovern/opencomply/services/integration/integration-type/digitalocean-team/configs" + doppler "github.com/opengovern/opencomply/services/integration/integration-type/doppler-account" + dopplerConfigs "github.com/opengovern/opencomply/services/integration/integration-type/doppler-account/configs" "github.com/opengovern/opencomply/services/integration/integration-type/entra-id-directory" entraidConfigs "github.com/opengovern/opencomply/services/integration/integration-type/entra-id-directory/configs" githubaccount "github.com/opengovern/opencomply/services/integration/integration-type/github-account" @@ -44,6 +46,7 @@ const ( IntegrationTypeGoogleWorkspaceAccount = googleConfig.IntegrationTypeGoogleWorkspaceAccount IntegrationTypeOCIRepository = ociConfigs.IntegrationTypeOciRepository IntegrationTypeRenderAccount = renderConfigs.IntegrationTypeRenderAccount + IntegrationTypeDopplerAccount = dopplerConfigs.IntegrationTypeDopplerAccount ) var AllIntegrationTypes = []integration.Type{ @@ -59,6 +62,7 @@ var AllIntegrationTypes = []integration.Type{ IntegrationTypeGoogleWorkspaceAccount, IntegrationTypeOCIRepository, IntegrationTypeRenderAccount, + IntegrationTypeDopplerAccount, } var IntegrationTypes = map[integration.Type]interfaces.IntegrationType{ @@ -74,6 +78,7 @@ var IntegrationTypes = map[integration.Type]interfaces.IntegrationType{ IntegrationTypeGoogleWorkspaceAccount: &google_workspace_account.GoogleWorkspaceAccountIntegration{}, IntegrationTypeOCIRepository: &oci.Integration{}, IntegrationTypeRenderAccount: &render.RenderAccountIntegration{}, + IntegrationTypeDopplerAccount: &doppler.DopplerAccountIntegration{}, } func ParseType(str string) integration.Type {