From d1be5fa8a0a856c18f6c0f8c91813efc6db70789 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 16 Dec 2024 19:02:38 +0330 Subject: [PATCH] feat: added doppler discovery and healthcheck --- .../doppler-account/configs/credentials.go | 2 +- .../configs/resource_types_list.go | 48 +++++++---- .../doppler_integration_discovery.go | 84 +++++++------------ .../doppler-account/doppler_account.go | 60 ++++++------- .../doppler_integration_healthcheck.go | 68 +++++++-------- .../integration-type/integrations.go | 5 ++ 6 files changed, 122 insertions(+), 145 deletions(-) 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 {