Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add sentry dsn in web config #973

Merged
merged 6 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions api/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
package api

import "time"
import (
"net/http"
"time"
)

// WebContact is container for web ui contact validation.
type WebContact struct {
ContactType string `json:"type" example:"webhook"`
ContactLabel string `json:"label" example:"Webhook"`
ValidationRegex string `json:"validation,omitempty" example:"^(http|https):\\/\\/.*(moira.ru)(:[0-9]{2,5})?\\/"`
Placeholder string `json:"placeholder,omitempty" example:"https://moira.ru/webhooks"`
Help string `json:"help,omitempty" example:"### Domains whitelist:\n - moira.ru\n"`
}

// FeatureFlags is struct to manage feature flags.
type FeatureFlags struct {
IsPlottingDefaultOn bool `json:"isPlottingDefaultOn" example:"false"`
IsPlottingAvailable bool `json:"isPlottingAvailable" example:"true"`
IsSubscriptionToAllTagsAvailable bool `json:"isSubscriptionToAllTagsAvailable" example:"false"`
IsReadonlyEnabled bool `json:"isReadonlyEnabled" example:"false"`
}

// Sentry - config for sentry settings
type Sentry struct {
kissken marked this conversation as resolved.
Show resolved Hide resolved
DSN string `json:"dsn,omitempty" example:"https://[email protected]"`
}

// Config for api configuration variables.
type Config struct {
Expand All @@ -14,25 +39,13 @@ type Config struct {

// WebConfig is container for web ui configuration parameters.
type WebConfig struct {
SupportEmail string `json:"supportEmail,omitempty"`
RemoteAllowed bool `json:"remoteAllowed"`
SupportEmail string `json:"supportEmail,omitempty" example:"[email protected]"`
RemoteAllowed bool `json:"remoteAllowed" example:"true"`
Contacts []WebContact `json:"contacts"`
FeatureFlags FeatureFlags `json:"featureFlags"`
Sentry Sentry `json:"sentry"`
}

// WebContact is container for web ui contact validation.
type WebContact struct {
ContactType string `json:"type"`
ContactLabel string `json:"label"`
ValidationRegex string `json:"validation,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
Help string `json:"help,omitempty"`
}

// FeatureFlags is struct to manage feature flags.
type FeatureFlags struct {
IsPlottingDefaultOn bool `json:"isPlottingDefaultOn"`
IsPlottingAvailable bool `json:"isPlottingAvailable"`
IsSubscriptionToAllTagsAvailable bool `json:"isSubscriptionToAllTagsAvailable"`
IsReadonlyEnabled bool `json:"isReadonlyEnabled"`
func (WebConfig) Render(w http.ResponseWriter, r *http.Request) error {
return nil
}
31 changes: 13 additions & 18 deletions api/handler/config.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
package handler

import "net/http"
import (
"net/http"

type ContactExample struct {
Type string `json:"type" example:"webhook kontur"`
Label string `json:"label" example:"Webhook Kontur"`
Validation string `json:"validation" example:"^(http|https):\\/\\/.*(moira.ru)(:[0-9]{2,5})?\\/"`
Placeholder string `json:"placeholder" example:"https://moira.ru/webhooks/moira"`
Help string `json:"help" example:"### Domains whitelist:\n - moira.ru\n"`
}

type ConfigurationResponse struct {
RemoteAllowed bool `json:"remoteAllowed" example:"false"`
Contacts []ContactExample `json:"contacts"`
}
"github.com/go-chi/render"
"github.com/moira-alert/moira/api"
)

// nolint: gofmt,goimports
//
// @summary Get available configuration
// @summary Get web configuration
// @id get-web-config
// @tags config
// @produce json
// @success 200 {object} ConfigurationResponse "Configuration fetched successfully"
// @success 200 {object} api.WebConfig "Configuration fetched successfully"
// @failure 422 {object} api.ErrorRenderExample "Render error"
// @router /config [get]
func getWebConfig(configContent []byte) http.HandlerFunc {
func getWebConfig(webConfig *api.WebConfig) http.HandlerFunc {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.Write(configContent) //nolint
if err := render.Render(writer, request, webConfig); err != nil {
render.Render(writer, request, api.ErrorRender(err)) //nolint
return
}
})
}
16 changes: 8 additions & 8 deletions api/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ func NewHandler(
db moira.Database,
log moira.Logger,
index moira.Searcher,
config *api.Config,
apiConfig *api.Config,
metricSourceProvider *metricSource.SourceProvider,
webConfigContent []byte,
webConfig *api.WebConfig,
) http.Handler {
database = db
searchIndex = index
Expand Down Expand Up @@ -94,13 +94,13 @@ func NewHandler(
router.Use(moiramiddle.DatabaseContext(database))
router.Route("/health", health)
router.Route("/", func(router chi.Router) {
router.Use(moiramiddle.ReadOnlyMiddleware(config))
router.Get("/config", getWebConfig(webConfigContent))
router.Use(moiramiddle.ReadOnlyMiddleware(apiConfig))
router.Get("/config", getWebConfig(webConfig))
router.Route("/user", user)
router.With(moiramiddle.Triggers(
config.GraphiteLocalMetricTTL,
config.GraphiteRemoteMetricTTL,
config.PrometheusRemoteMetricTTL,
apiConfig.GraphiteLocalMetricTTL,
apiConfig.GraphiteRemoteMetricTTL,
apiConfig.PrometheusRemoteMetricTTL,
)).Route("/trigger", triggers(metricSourceProvider, searchIndex))
router.Route("/tag", tag)
router.Route("/pattern", pattern)
Expand All @@ -118,7 +118,7 @@ func NewHandler(
})
})

if config.EnableCORS {
if apiConfig.EnableCORS {
return cors.AllowAll().Handler(router)
}
return router
Expand Down
18 changes: 14 additions & 4 deletions api/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/golang/mock/gomock"
Expand All @@ -27,8 +28,11 @@ func TestReadonlyMode(t *testing.T) {

logger, _ := zerolog_adapter.GetLogger("Test")
config := &api.Config{Flags: api.FeatureFlags{IsReadonlyEnabled: true}}
expectedConfig := []byte("Expected config")
handler := NewHandler(mockDb, logger, nil, config, nil, expectedConfig)
webConfig := &api.WebConfig{
SupportEmail: "test",
Contacts: []api.WebContact{},
}
handler := NewHandler(mockDb, logger, nil, config, nil, webConfig)

Convey("Get notifier health", func() {
mockDb.EXPECT().GetNotifierState().Return("OK", nil).Times(1)
Expand Down Expand Up @@ -92,10 +96,16 @@ func TestReadonlyMode(t *testing.T) {

response := responseWriter.Result()
defer response.Body.Close()
actual, _ := io.ReadAll(response.Body)
actual, err := io.ReadAll(response.Body)
So(err, ShouldBeNil)
actualStr := strings.TrimSpace(string(actual))

expected, err := json.Marshal(webConfig)
So(err, ShouldBeNil)
expectedStr := strings.TrimSpace(string(expected))

So(response.StatusCode, ShouldEqual, http.StatusOK)
So(actual, ShouldResemble, expectedConfig)
So(actualStr, ShouldResemble, expectedStr)
})
})
}
29 changes: 18 additions & 11 deletions cmd/api/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package main

import (
"encoding/json"
"fmt"

"github.com/moira-alert/moira/notifier"

"github.com/xiam/to"
Expand All @@ -30,15 +27,27 @@ type apiConfig struct {
EnableCORS bool `yaml:"enable_cors"`
}

type sentryConfig struct {
DSN string `yaml:"dsn"`
}

func (config *sentryConfig) getSettings() api.Sentry {
return api.Sentry{
DSN: config.DSN,
}
}

type webConfig struct {
// Moira administrator email address.
// Moira administrator email address
SupportEmail string `yaml:"supportEmail"`
// If true, users will be able to choose Graphite as trigger metrics data source
RemoteAllowed bool
// List of enabled contact types
Contacts []webContact `yaml:"contacts"`
// struct to manage feature flags.
// Struct to manage feature flags
FeatureFlags featureFlags `yaml:"feature_flags"`
// Returns the sentry configuration for the frontend
Sentry sentryConfig `yaml:"sentry"`
}

type webContact struct {
Expand Down Expand Up @@ -75,7 +84,7 @@ func (config *apiConfig) getSettings(
}
}

func (config *webConfig) getSettings(isRemoteEnabled bool) ([]byte, error) {
func (config *webConfig) getSettings(isRemoteEnabled bool) *api.WebConfig {
webContacts := make([]api.WebContact, 0, len(config.Contacts))
for _, configContact := range config.Contacts {
contact := api.WebContact{
Expand All @@ -87,16 +96,14 @@ func (config *webConfig) getSettings(isRemoteEnabled bool) ([]byte, error) {
}
webContacts = append(webContacts, contact)
}
configContent, err := json.Marshal(api.WebConfig{

return &api.WebConfig{
SupportEmail: config.SupportEmail,
RemoteAllowed: isRemoteEnabled,
Contacts: webContacts,
FeatureFlags: config.getFeatureFlags(),
})
if err != nil {
return make([]byte, 0), fmt.Errorf("failed to parse web config: %s", err.Error())
Sentry: config.Sentry.getSettings(),
}
return configContent, nil
}

func (config *webConfig) getFeatureFlags() api.FeatureFlags {
Expand Down
56 changes: 44 additions & 12 deletions cmd/api/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,23 +112,32 @@ func Test_webConfig_getDefault(t *testing.T) {

func Test_webConfig_getSettings(t *testing.T) {
Convey("Empty config, fill it", t, func() {
wC := webConfig{}
config := webConfig{}

result, err := wC.getSettings(true)
So(err, ShouldBeEmpty)
So(string(result), ShouldResemble, `{"remoteAllowed":true,"contacts":[],"featureFlags":{"isPlottingDefaultOn":false,"isPlottingAvailable":false,"isSubscriptionToAllTagsAvailable":false,"isReadonlyEnabled":false}}`)
settings := config.getSettings(true)
So(settings, ShouldResemble, &api.WebConfig{
RemoteAllowed: true,
Contacts: []api.WebContact{},
})
})

Convey("Default config, fill it", t, func() {
config := getDefault()

result, err := config.Web.getSettings(true)
So(err, ShouldBeEmpty)
So(string(result), ShouldResemble, `{"remoteAllowed":true,"contacts":[],"featureFlags":{"isPlottingDefaultOn":true,"isPlottingAvailable":true,"isSubscriptionToAllTagsAvailable":true,"isReadonlyEnabled":false}}`)
settings := config.Web.getSettings(true)
So(settings, ShouldResemble, &api.WebConfig{
RemoteAllowed: true,
Contacts: []api.WebContact{},
FeatureFlags: api.FeatureFlags{
IsPlottingDefaultOn: true,
IsPlottingAvailable: true,
IsSubscriptionToAllTagsAvailable: true,
},
})
})

Convey("Not empty config, fill it", t, func() {
wC := webConfig{
config := webConfig{
SupportEmail: "[email protected]",
RemoteAllowed: false,
Contacts: []webContact{
Expand All @@ -142,13 +151,36 @@ func Test_webConfig_getSettings(t *testing.T) {
},
FeatureFlags: featureFlags{
IsPlottingDefaultOn: true,
IsPlottingAvailable: false,
IsPlottingAvailable: true,
IsSubscriptionToAllTagsAvailable: true,
IsReadonlyEnabled: false,
},
Sentry: sentryConfig{
DSN: "test dsn",
},
}

result, err := wC.getSettings(true)
So(err, ShouldBeEmpty)
So(string(result), ShouldResemble, `{"supportEmail":"[email protected]","remoteAllowed":true,"contacts":[{"type":"slack","label":"label","validation":"t(\\d+)","help":"help"}],"featureFlags":{"isPlottingDefaultOn":true,"isPlottingAvailable":false,"isSubscriptionToAllTagsAvailable":true,"isReadonlyEnabled":false}}`)
settings := config.getSettings(true)
So(settings, ShouldResemble, &api.WebConfig{
SupportEmail: "[email protected]",
RemoteAllowed: true,
Contacts: []api.WebContact{
{
ContactType: "slack",
ContactLabel: "label",
ValidationRegex: "t(\\d+)",
Help: "help",
},
},
FeatureFlags: api.FeatureFlags{
IsPlottingDefaultOn: true,
IsPlottingAvailable: true,
IsSubscriptionToAllTagsAvailable: true,
IsReadonlyEnabled: false,
},
Sentry: api.Sentry{
DSN: "test dsn",
},
})
})
}
9 changes: 2 additions & 7 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,15 @@ func main() {
prometheusSource,
)

webConfigContent, err := applicationConfig.Web.getSettings(remoteConfig.Enabled || prometheusConfig.Enabled)
if err != nil {
logger.Fatal().
Error(err).
Msg("Failed to get web applicationConfig content ")
}
webConfig := applicationConfig.Web.getSettings(remoteConfig.Enabled || prometheusConfig.Enabled)

httpHandler := handler.NewHandler(
database,
logger,
searchIndex,
apiConfig,
metricSourceProvider,
webConfigContent,
webConfig,
)

server := &http.Server{
Expand Down
Loading