openreplay/backend/pkg/integrations/clients/dynatrace.go
Alexander d2a5f42e37
Dynatrace mock (#2745)
* feat(web): small changes in dynatrace + mock for testing

* feat(backend): undo dynatrace mock

* feat(backend): removed commented code from page builder

* feat(backend): added 404 for not found sql response in integrations
2024-11-14 16:06:17 +01:00

154 lines
4 KiB
Go

package clients
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
type dynatraceClient struct{}
func NewDynatraceClient() Client {
return &dynatraceClient{}
}
type dynatraceConfig struct {
Environment string `json:"environment"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
Resource string `json:"resource"`
}
type AuthToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
func (d *dynatraceClient) FetchSessionData(credentials interface{}, sessionID uint64) (interface{}, error) {
// Try to parse the credentials as a Config struct
cfg, ok := credentials.(dynatraceConfig)
if !ok {
strCfg, ok := credentials.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("invalid credentials, got: %+v", credentials)
}
cfg = dynatraceConfig{}
if val, ok := strCfg["environment"].(string); ok {
cfg.Environment = val
}
if val, ok := strCfg["client_id"].(string); ok {
cfg.ClientID = val
}
if val, ok := strCfg["client_secret"].(string); ok {
cfg.ClientSecret = val
}
if val, ok := strCfg["resource"].(string); ok {
cfg.Resource = val
}
}
token, err := d.requestOAuthToken(cfg.ClientID, cfg.ClientSecret, cfg.Resource)
if err != nil {
return nil, fmt.Errorf("error while requesting oauth token: %w", err)
}
logs, err := d.requestLogs(token.AccessToken, cfg.Environment, sessionID)
if err != nil {
return nil, fmt.Errorf("error while requesting logs: %w", err)
}
return logs, nil
}
func (d *dynatraceClient) requestOAuthToken(clientID, clientSecret, resource string) (*AuthToken, error) {
requestURL := "https://sso.dynatrace.com/sso/oauth2/token"
params := url.Values{}
params.Add("grant_type", "client_credentials")
params.Add("client_id", clientID)
params.Add("client_secret", clientSecret)
params.Add("resource", resource)
params.Add("scope", "storage:buckets:read storage:logs:read")
requestURL += "?" + params.Encode()
request, err := http.NewRequest("POST", requestURL, nil)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
newToken := &AuthToken{}
err = json.Unmarshal(body, newToken)
if err != nil {
return nil, err
}
if newToken.AccessToken == "" {
return nil, fmt.Errorf("empty access token, body: %s", string(body))
}
return newToken, nil
}
type Logs struct {
Results []interface{} `json:"results"`
}
func testRequestParams() url.Values {
params := url.Values{}
params.Add("limit", "1")
return params
}
func requestParams(sessionID uint64) url.Values {
params := url.Values{}
params.Add("limit", "1000")
params.Add("query", "(status=\"WARN\" OR status=\"ERROR\") AND openReplaySession.id="+fmt.Sprint(sessionID))
return params
}
func (d *dynatraceClient) requestLogs(token, environmentID string, sessionID uint64) (interface{}, error) {
requestURL := fmt.Sprintf("https://%s.live.dynatrace.com/api/v2/logs/search", environmentID)
if sessionID == 0 {
requestURL += "?" + testRequestParams().Encode()
} else {
requestURL += "?" + requestParams(sessionID).Encode()
}
request, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
logs := &Logs{}
err = json.Unmarshal(body, logs)
if err != nil {
return nil, err
}
if len(logs.Results) == 0 {
return nil, fmt.Errorf("empty logs, body: %s", string(body))
}
responseContent, _ := json.Marshal(logs.Results)
return responseContent, nil
}