* 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
154 lines
4 KiB
Go
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
|
|
}
|