Compare commits

..

40 Commits

Author SHA1 Message Date
grandwizard28
72c7fd3454 feat(signoz): add backwards compatibility function 2024-11-19 18:02:27 +05:30
grandwizard28
761eed7be8 feat(SIGNOZ_JWT_SECRET): add deprecation warning 2024-11-08 20:37:47 +05:30
grandwizard28
96bf51a951 feat(config): add a backwards compatibility function 2024-11-08 19:02:26 +05:30
grandwizard28
cd4085d134 chore(defaults): address comments 2024-11-08 17:23:18 +05:30
grandwizard28
1758c7b7be feat(go): run go-mod tidy 2024-11-08 17:09:54 +05:30
grandwizard28
1680fe1c96 Merge branch 'develop' into 176/coalesce
# Conflicts:
#	go.mod
2024-11-08 17:06:31 +05:30
grandwizard28
23db133b74 refactor(healthcheck): remove healthcheck 2024-08-23 15:43:47 +05:30
grandwizard28
d70e400605 refactor(dynamic_config): deprecate dynamic_config 2024-08-23 15:40:40 +05:30
grandwizard28
b6e2c26022 refactor(version): deprecate version 2024-08-23 15:38:28 +05:30
grandwizard28
f0c054f372 Merge branch 'develop' into 176/coalesce 2024-08-23 14:50:10 +05:30
grandwizard28
83b8c5ad2e feat(http): move config 2024-08-23 00:10:29 +05:30
grandwizard28
487935817a feat(opamp): move config 2024-08-23 00:04:14 +05:30
grandwizard28
c047abb951 feat(storage): move config 2024-08-22 23:59:35 +05:30
grandwizard28
633c5dbc83 feat(database): move config 2024-08-22 23:51:31 +05:30
grandwizard28
ed3180f84c feat(auth): move auth config 2024-08-22 23:41:56 +05:30
grandwizard28
6b6512c993 feat(cache): move cache config 2024-08-22 23:35:12 +05:30
grandwizard28
df4e5c043f feat(conf): add web, instrumentation to conf 2024-08-22 23:27:09 +05:30
grandwizard28
72c6d7c3d7 Merge branch '176/render' into 176/coalesce 2024-08-22 20:58:04 +05:30
grandwizard28
e827b6993d Merge branch 'develop' into 176/render 2024-08-22 20:58:02 +05:30
grandwizard28
77dd908fde feat(conf): create a default config file 2024-08-22 20:53:10 +05:30
grandwizard28
949b71837e feat(cmd): add cobra command for signoz entrypoint 2024-08-22 20:52:54 +05:30
grandwizard28
8a92a81416 fix(config): read defaults for config keys which do not exist 2024-08-22 20:52:34 +05:30
grandwizard28
3fd8c67999 feat(version): add pretty print 2024-08-22 20:52:12 +05:30
grandwizard28
0dccd20ea0 Merge branch '176/web' into 176/render 2024-08-22 17:51:34 +05:30
grandwizard28
fb78937956 feat(render): add render package 2024-08-22 17:13:10 +05:30
grandwizard28
f9aa333c4d Merge branch 'develop' into 176/web 2024-08-22 15:26:50 +05:30
grandwizard28
062b87c11d Merge branch '176/error' into 176/web 2024-08-22 14:33:01 +05:30
grandwizard28
3d61df370c Merge branch '176/registry' into 176/error 2024-08-22 14:33:00 +05:30
grandwizard28
22005f1561 Merge branch 'develop' into 176/registry 2024-08-22 14:32:59 +05:30
grandwizard28
ba0c896e87 ci(git): add git-town to .gitignore 2024-08-22 14:25:52 +05:30
grandwizard28
b74843c380 feat(errors): add the revamped error model 2024-08-22 14:25:52 +05:30
grandwizard28
3496ec2bb2 feat(web): add unit test for config 2024-08-22 14:25:52 +05:30
grandwizard28
9e4b3718ba feat(web): add config in master config package 2024-08-22 14:25:52 +05:30
grandwizard28
863c86d33f feat(web): add web package 2024-08-22 14:25:52 +05:30
grandwizard28
02d337da6b feat(middleware): add cache middleware 2024-08-22 14:25:52 +05:30
grandwizard28
dd0dd0167c feat(errors): add errors package 2024-08-22 14:25:51 +05:30
grandwizard28
b15db358cc feat(timeout): make max timeout configurable, move to a single log statement in logging 2024-08-22 13:28:54 +05:30
grandwizard28
dd06ad44d6 ci(git): add git-town to .gitignore 2024-08-21 21:02:28 +05:30
grandwizard28
29b33cf13c feat(registry): add registry and http package 2024-08-21 21:00:07 +05:30
grandwizard28
b06a24f2d6 feat(registry): add registry package 2024-08-21 21:00:06 +05:30
81 changed files with 1345 additions and 3698 deletions

29
cmd/signoz/config.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"flag"
"strings"
"github.com/spf13/cobra"
)
type config struct {
uris []string
}
func (c *config) Set(val string) error {
c.uris = append(c.uris, val)
return nil
}
func (c *config) String() string {
return "[" + strings.Join(c.uris, ", ") + "]"
}
func (c *config) registerFlags(cmd *cobra.Command) {
flagSet := new(flag.FlagSet)
flagSet.Var(c, "config", "Locations to the config file(s), note that only a"+
" single location can be set per flag entry e.g. `--config=file:/path/to/first --config=file:path/to/second`.")
cmd.Flags().AddGoFlagSet(flagSet)
}

101
cmd/signoz/main.go Normal file
View File

@@ -0,0 +1,101 @@
package main
import (
"context"
"os"
"slices"
"github.com/spf13/cobra"
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/provider/fileprovider"
signozconfig "go.signoz.io/signoz/pkg/config"
"go.signoz.io/signoz/pkg/confmap/provider/signozenvprovider"
"go.signoz.io/signoz/pkg/instrumentation"
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/version"
"go.signoz.io/signoz/pkg/web"
"go.uber.org/zap"
)
func main() {
var config config
app := &cobra.Command{
Use: "signoz",
Short: "An open-source observability platform native to OpenTelemetry with logs, traces and metrics in a single application.",
Version: version.Info.Version,
SilenceUsage: true,
SilenceErrors: false,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
return run(ctx, config)
},
PostRunE: func(cmd *cobra.Command, args []string) error {
return nil
},
}
// register all flags
config.registerFlags(app)
// register the Name in version as per the command name
version.Info.Name = app.Use
if err := app.Execute(); err != nil {
os.Exit(1)
}
}
func run(
ctx context.Context,
cfg config,
) error {
// Create the master config first. The master config will be used to initialize/create
// all other components
if len(cfg.uris) == 0 || !slices.Contains(cfg.uris, "signozenv:") {
cfg.uris = append(cfg.uris, "signozenv:")
}
config, err := signozconfig.New(ctx, signozconfig.ProviderSettings{
ResolverSettings: confmap.ResolverSettings{
URIs: cfg.uris,
ProviderFactories: []confmap.ProviderFactory{
signozenvprovider.NewFactory(),
fileprovider.NewFactory(),
},
},
})
if err != nil {
return err
}
// Print the current environment
if config.Version.Banner.Enabled {
version.Info.PrettyPrint()
}
// Instrument the application, here we finally get the logger
instr, err := instrumentation.New(ctx, version.Info, config.Instrumentation)
if err != nil {
return err
}
// Remove this function once the new config is completely rolled out
signozconfig.EnsureBackwardsCompatibility(ctx, instr, config)
// To support backward compatibility, we are going to initialize the global zap logger
zap.ReplaceGlobals(instr.Logger.Named("go.signoz.io/signoz/cmd/signoz"))
_, err = web.New(instr.Logger, config.Web)
if err != nil {
return err
}
auth.JwtSecret = config.Auth.Jwt.Secret
if len(auth.JwtSecret) == 0 {
instr.Logger.Warn("no jwt secret key is specified", zap.Any("context", ctx))
}
return nil
}

125
conf/defaults.yaml Normal file
View File

@@ -0,0 +1,125 @@
##################### SigNoz Configuration Defaults #####################
#
# Do not modify this file
#
##################### Version #####################
version:
banner:
# Whether to show the banner on startup
enabled: true
##################### Instrumentation #####################
instrumentation:
logs:
# Whether to enable log exports to an otlp compatible backend
enabled: false
# the level of the logger
level: error
processors:
- batch:
exporter:
otlp:
# the otlp endpoint
endpoint:
# the otlp protocol
protocol:
traces:
# Whether to enable trace exports to an otlp compatible backend
enabled: false
processors:
- batch:
exporter:
otlp:
# the otlp endpoint
endpoint:
# the otlp protocol
protocol:
metrics:
# Whether to enable metric exports to an otlp compatible backend or in prometheus format
enabled: true
readers:
- periodic:
exporter:
otlp:
# the otlp endpoint
endpoint:
# the otlp protocol
protocol:
prometheus:
host: 0.0.0.0
port: 9090
##################### Web #####################
web:
# The prefix to serve web on
prefix: /
# The directory containing the static build files.
directory: /etc/signoz/web
##################### Cache #####################
cache:
# Memory provider
provider: memory
memory:
ttl: -1
cleanup_interval: 60
# Redis Provider
# provider: redis
# redis:
# host: localhost
# port: 6379
# password: password
# db: 0
##################### Auth #####################
auth:
jwt:
# The JWT secret for authentication
secret: secret
##################### Database #####################
database:
# Supported options are sqlite
provider: sqlite
# Path to the sqlite database
sqlite:
path: signoz.db
max_open_conn: 10
##################### Storage #####################
storage:
provider: clickhouse
clickhouse:
dsn: tcp://localhost:9000
cluster: cluster
max_idle_connections:
max_open_connections:
dial_timeout:
settings:
optimize_read_in_order_regex:
max_execution_time_leaf:
timeout_before_checking_execution_speed:
max_bytes_to_read:
##################### Opamp #####################
opamp:
address: 0.0.0.0:4320
##################### Http #####################
http:
address: 0.0.0.0:8000
timeout: 60s
max_timeout: 600s
##################### Prometheus #####################
prometheus:
remote_read:
config:
url: tcp://localhost:9000/signoz_metrics
##################### Alerting #####################
alerting:
rules:
enabled: true

View File

@@ -18,7 +18,7 @@ import (
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
rules "go.signoz.io/signoz/pkg/query-service/rules"
"go.signoz.io/signoz/pkg/query-service/version"
"go.signoz.io/signoz/pkg/version"
)
type APIHandlerOptions struct {
@@ -40,7 +40,6 @@ type APIHandlerOptions struct {
// Querier Influx Interval
FluxInterval time.Duration
UseLogsNewSchema bool
UseLicensesV3 bool
}
type APIHandler struct {
@@ -66,7 +65,6 @@ func NewAPIHandler(opts APIHandlerOptions) (*APIHandler, error) {
Cache: opts.Cache,
FluxInterval: opts.FluxInterval,
UseLogsNewSchema: opts.UseLogsNewSchema,
UseLicensesV3: opts.UseLicensesV3,
})
if err != nil {
@@ -175,25 +173,10 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew
router.HandleFunc("/api/v1/dashboards/{uuid}/lock", am.EditAccess(ah.lockDashboard)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/dashboards/{uuid}/unlock", am.EditAccess(ah.unlockDashboard)).Methods(http.MethodPut)
// v2
router.HandleFunc("/api/v2/licenses",
am.ViewAccess(ah.listLicensesV2)).
Methods(http.MethodGet)
// v3
router.HandleFunc("/api/v3/licenses",
am.ViewAccess(ah.listLicensesV3)).
Methods(http.MethodGet)
router.HandleFunc("/api/v3/licenses",
am.AdminAccess(ah.applyLicenseV3)).
Methods(http.MethodPost)
router.HandleFunc("/api/v3/licenses",
am.AdminAccess(ah.refreshLicensesV3)).
Methods(http.MethodPut)
// v4
router.HandleFunc("/api/v4/query_range", am.ViewAccess(ah.queryRangeV4)).Methods(http.MethodPost)
// Gateway
@@ -204,9 +187,8 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew
}
func (ah *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
version := version.GetVersion()
versionResponse := basemodel.GetVersionResponse{
Version: version,
Version: version.Info.Version,
EE: "Y",
SetupCompleted: ah.SetupCompleted,
}

View File

@@ -9,7 +9,6 @@ import (
"go.signoz.io/signoz/ee/query-service/constants"
"go.signoz.io/signoz/ee/query-service/model"
"go.signoz.io/signoz/pkg/http/render"
"go.uber.org/zap"
)
@@ -60,21 +59,6 @@ type billingDetails struct {
} `json:"data"`
}
type ApplyLicenseRequest struct {
LicenseKey string `json:"key"`
}
type ListLicenseResponse map[string]interface{}
func convertLicenseV3ToListLicenseResponse(licensesV3 []*model.LicenseV3) []ListLicenseResponse {
listLicenses := []ListLicenseResponse{}
for _, license := range licensesV3 {
listLicenses = append(listLicenses, license.Data)
}
return listLicenses
}
func (ah *APIHandler) listLicenses(w http.ResponseWriter, r *http.Request) {
licenses, apiError := ah.LM().GetLicenses(context.Background())
if apiError != nil {
@@ -104,51 +88,6 @@ func (ah *APIHandler) applyLicense(w http.ResponseWriter, r *http.Request) {
ah.Respond(w, license)
}
func (ah *APIHandler) listLicensesV3(w http.ResponseWriter, r *http.Request) {
licenses, apiError := ah.LM().GetLicensesV3(r.Context())
if apiError != nil {
RespondError(w, apiError, nil)
return
}
ah.Respond(w, convertLicenseV3ToListLicenseResponse(licenses))
}
// this function is called by zeus when inserting licenses in the query-service
func (ah *APIHandler) applyLicenseV3(w http.ResponseWriter, r *http.Request) {
var licenseKey ApplyLicenseRequest
if err := json.NewDecoder(r.Body).Decode(&licenseKey); err != nil {
RespondError(w, model.BadRequest(err), nil)
return
}
if licenseKey.LicenseKey == "" {
RespondError(w, model.BadRequest(fmt.Errorf("license key is required")), nil)
return
}
_, apiError := ah.LM().ActivateV3(r.Context(), licenseKey.LicenseKey)
if apiError != nil {
RespondError(w, apiError, nil)
return
}
render.Success(w, http.StatusAccepted, nil)
}
func (ah *APIHandler) refreshLicensesV3(w http.ResponseWriter, r *http.Request) {
apiError := ah.LM().RefreshLicense(r.Context())
if apiError != nil {
RespondError(w, apiError, nil)
return
}
render.Success(w, http.StatusNoContent, nil)
}
func (ah *APIHandler) checkout(w http.ResponseWriter, r *http.Request) {
type checkoutResponse struct {
@@ -215,45 +154,11 @@ func (ah *APIHandler) getBilling(w http.ResponseWriter, r *http.Request) {
ah.Respond(w, billingResponse.Data)
}
func convertLicenseV3ToLicenseV2(licenses []*model.LicenseV3) []model.License {
licensesV2 := []model.License{}
for _, l := range licenses {
licenseV2 := model.License{
Key: l.Key,
ActivationId: "",
PlanDetails: "",
FeatureSet: l.Features,
ValidationMessage: "",
IsCurrent: l.IsCurrent,
LicensePlan: model.LicensePlan{
PlanKey: l.PlanName,
ValidFrom: l.ValidFrom,
ValidUntil: l.ValidUntil,
Status: l.Status},
}
licensesV2 = append(licensesV2, licenseV2)
}
return licensesV2
}
func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) {
var licenses []model.License
if ah.UseLicensesV3 {
licensesV3, err := ah.LM().GetLicensesV3(r.Context())
if err != nil {
RespondError(w, err, nil)
return
}
licenses = convertLicenseV3ToLicenseV2(licensesV3)
} else {
_licenses, apiError := ah.LM().GetLicenses(r.Context())
if apiError != nil {
RespondError(w, apiError, nil)
return
}
licenses = _licenses
licenses, apiError := ah.LM().GetLicenses(context.Background())
if apiError != nil {
RespondError(w, apiError, nil)
}
resp := model.Licenses{

View File

@@ -31,6 +31,7 @@ import (
"go.signoz.io/signoz/ee/query-service/rules"
baseauth "go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/migrate"
"go.signoz.io/signoz/pkg/query-service/model"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
licensepkg "go.signoz.io/signoz/ee/query-service/license"
@@ -47,7 +48,6 @@ import (
"go.signoz.io/signoz/pkg/query-service/app/preferences"
"go.signoz.io/signoz/pkg/query-service/cache"
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/healthcheck"
basealm "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
@@ -77,7 +77,6 @@ type ServerOptions struct {
Cluster string
GatewayUrl string
UseLogsNewSchema bool
UseLicensesV3 bool
}
// Server runs HTTP api service
@@ -97,13 +96,6 @@ type Server struct {
usageManager *usage.Manager
opampServer *opamp.Server
unavailableChannel chan healthcheck.Status
}
// HealthCheckStatus returns health check status channel a client can subscribe to
func (s Server) HealthCheckStatus() chan healthcheck.Status {
return s.unavailableChannel
}
// NewServer creates and initializes Server
@@ -134,7 +126,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
}
// initiate license manager
lm, err := licensepkg.StartManager("sqlite", localDB, serverOptions.UseLicensesV3)
lm, err := licensepkg.StartManager("sqlite", localDB)
if err != nil {
return nil, err
}
@@ -144,6 +136,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
readerReady := make(chan bool)
var reader interfaces.DataConnector
// todo(remove): read from config
storage := os.Getenv("STORAGE")
if storage == "clickhouse" {
zap.L().Info("Using ClickHouse as datastore ...")
@@ -270,7 +263,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
FluxInterval: fluxInterval,
Gateway: gatewayProxy,
UseLogsNewSchema: serverOptions.UseLogsNewSchema,
UseLicensesV3: serverOptions.UseLicensesV3,
}
apiHandler, err := api.NewAPIHandler(apiOpts)
@@ -281,10 +273,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
s := &Server{
// logger: logger,
// tracer: tracer,
ruleManager: rm,
serverOptions: serverOptions,
unavailableChannel: make(chan healthcheck.Status),
usageManager: usageManager,
ruleManager: rm,
serverOptions: serverOptions,
usageManager: usageManager,
}
httpServer, err := s.createPublicServer(apiHandler)
@@ -349,7 +340,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e
}
if user.User.OrgId == "" {
return nil, basemodel.UnauthorizedError(errors.New("orgId is missing in the claims"))
return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims"))
}
return user, nil
@@ -659,7 +650,6 @@ func (s *Server) Start() error {
default:
zap.L().Error("Could not start HTTP server", zap.Error(err))
}
s.unavailableChannel <- healthcheck.Unavailable
}()
go func() {
@@ -687,8 +677,6 @@ func (s *Server) Start() error {
zap.L().Error("Could not start private HTTP server", zap.Error(err))
}
s.unavailableChannel <- healthcheck.Unavailable
}()
go func() {
@@ -696,7 +684,6 @@ func (s *Server) Start() error {
err := s.opampServer.Start(baseconst.OpAmpWsEndpoint)
if err != nil {
zap.L().Error("opamp ws server failed to start", zap.Error(err))
s.unavailableChannel <- healthcheck.Unavailable
}
}()
@@ -766,9 +753,8 @@ func makeRulesManager(
Cache: cache,
EvalDelay: baseconst.GetEvalDelay(),
PrepareTaskFunc: rules.PrepareTaskFunc,
PrepareTestRuleFunc: rules.TestNotification,
UseLogsNewSchema: useLogsNewSchema,
PrepareTaskFunc: rules.PrepareTaskFunc,
UseLogsNewSchema: useLogsNewSchema,
}
// create Manager

View File

@@ -13,7 +13,6 @@ var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "")
var SaasSegmentKey = GetOrDefaultEnv("SIGNOZ_SAAS_SEGMENT_KEY", "")
var FetchFeatures = GetOrDefaultEnv("FETCH_FEATURES", "false")
var ZeusFeaturesURL = GetOrDefaultEnv("ZEUS_FEATURES_URL", "ZeusFeaturesURL")
var ZeusURL = GetOrDefaultEnv("ZEUS_URL", "ZeusURL")
func GetOrDefaultEnv(key string, fallback string) string {
v := os.Getenv(key)

View File

@@ -13,8 +13,3 @@ type ActivationResponse struct {
ActivationId string `json:"ActivationId"`
PlanDetails string `json:"PlanDetails"`
}
type ValidateLicenseResponse struct {
Status status `json:"status"`
Data map[string]interface{} `json:"data"`
}

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"io"
"net/http"
"time"
"github.com/pkg/errors"
"go.uber.org/zap"
@@ -24,14 +23,12 @@ const (
)
type Client struct {
Prefix string
GatewayUrl string
Prefix string
}
func New() *Client {
return &Client{
Prefix: constants.LicenseSignozIo,
GatewayUrl: constants.ZeusURL,
Prefix: constants.LicenseSignozIo,
}
}
@@ -119,60 +116,6 @@ func ValidateLicense(activationId string) (*ActivationResponse, *model.ApiError)
}
func ValidateLicenseV3(licenseKey string) (*model.LicenseV3, *model.ApiError) {
// Creating an HTTP client with a timeout for better control
client := &http.Client{
Timeout: 10 * time.Second,
}
req, err := http.NewRequest("GET", C.GatewayUrl+"/v2/licenses/me", nil)
if err != nil {
return nil, model.BadRequest(errors.Wrap(err, fmt.Sprintf("failed to create request: %w", err)))
}
// Setting the custom header
req.Header.Set("X-Signoz-Cloud-Api-Key", licenseKey)
response, err := client.Do(req)
if err != nil {
return nil, model.BadRequest(errors.Wrap(err, fmt.Sprintf("failed to make post request: %w", err)))
}
body, err := io.ReadAll(response.Body)
if err != nil {
return nil, model.BadRequest(errors.Wrap(err, fmt.Sprintf("failed to read validation response from %v", C.GatewayUrl)))
}
defer response.Body.Close()
switch response.StatusCode {
case 200:
a := ValidateLicenseResponse{}
err = json.Unmarshal(body, &a)
if err != nil {
return nil, model.BadRequest(errors.Wrap(err, "failed to marshal license validation response"))
}
license, err := model.NewLicenseV3(a.Data)
if err != nil {
return nil, model.BadRequest(errors.Wrap(err, "failed to generate new license v3"))
}
return license, nil
case 400:
return nil, model.BadRequest(errors.Wrap(fmt.Errorf(string(body)),
fmt.Sprintf("bad request error received from %v", C.GatewayUrl)))
case 401:
return nil, model.Unauthorized(errors.Wrap(fmt.Errorf(string(body)),
fmt.Sprintf("unauthorized request error received from %v", C.GatewayUrl)))
default:
return nil, model.InternalError(errors.Wrap(fmt.Errorf(string(body)),
fmt.Sprintf("internal request error received from %v", C.GatewayUrl)))
}
}
func NewPostRequestWithCtx(ctx context.Context, url string, contentType string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, POST, url, body)
if err != nil {

View File

@@ -3,7 +3,6 @@ package license
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
@@ -49,34 +48,6 @@ func (r *Repo) GetLicenses(ctx context.Context) ([]model.License, error) {
return licenses, nil
}
func (r *Repo) GetLicensesV3(ctx context.Context) ([]*model.LicenseV3, error) {
licensesData := []model.LicenseDB{}
licenseV3Data := []*model.LicenseV3{}
query := "SELECT id,key,data FROM licenses_v3"
err := r.db.Select(&licensesData, query)
if err != nil {
return nil, fmt.Errorf("failed to get licenses from db: %v", err)
}
for _, l := range licensesData {
var licenseData map[string]interface{}
err := json.Unmarshal([]byte(l.Data), &licenseData)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal data into licenseData : %v", err)
}
license, err := model.NewLicenseV3WithIDAndKey(l.ID, l.Key, licenseData)
if err != nil {
return nil, fmt.Errorf("failed to get licenses v3 schema : %v", err)
}
licenseV3Data = append(licenseV3Data, license)
}
return licenseV3Data, nil
}
// GetActiveLicense fetches the latest active license from DB.
// If the license is not present, expect a nil license and a nil error in the output.
func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, *basemodel.ApiError) {
@@ -108,45 +79,6 @@ func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, *basemodel
return active, nil
}
func (r *Repo) GetActiveLicenseV3(ctx context.Context) (*model.LicenseV3, error) {
var err error
licenses := []model.LicenseDB{}
query := "SELECT id,key,data FROM licenses_v3"
err = r.db.Select(&licenses, query)
if err != nil {
return nil, basemodel.InternalError(fmt.Errorf("failed to get active licenses from db: %v", err))
}
var active *model.LicenseV3
for _, l := range licenses {
var licenseData map[string]interface{}
err := json.Unmarshal([]byte(l.Data), &licenseData)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal data into licenseData : %v", err)
}
license, err := model.NewLicenseV3WithIDAndKey(l.ID, l.Key, licenseData)
if err != nil {
return nil, fmt.Errorf("failed to get licenses v3 schema : %v", err)
}
if active == nil &&
(license.ValidFrom != 0) &&
(license.ValidUntil == -1 || license.ValidUntil > time.Now().Unix()) {
active = license
}
if active != nil &&
license.ValidFrom > active.ValidFrom &&
(license.ValidUntil == -1 || license.ValidUntil > time.Now().Unix()) {
active = license
}
}
return active, nil
}
// InsertLicense inserts a new license in db
func (r *Repo) InsertLicense(ctx context.Context, l *model.License) error {
@@ -272,53 +204,3 @@ func (r *Repo) InitFeatures(req basemodel.FeatureSet) error {
}
return nil
}
// InsertLicenseV3 inserts a new license v3 in db
func (r *Repo) InsertLicenseV3(ctx context.Context, l *model.LicenseV3) error {
query := `INSERT INTO licenses_v3 (id, key, data) VALUES ($1, $2, $3)`
// licsense is the entity of zeus so putting the entire license here without defining schema
licenseData, err := json.Marshal(l.Data)
if err != nil {
return fmt.Errorf("insert license failed: license marshal error")
}
_, err = r.db.ExecContext(ctx,
query,
l.ID,
l.Key,
string(licenseData),
)
if err != nil {
zap.L().Error("error in inserting license data: ", zap.Error(err))
return fmt.Errorf("failed to insert license in db: %v", err)
}
return nil
}
// UpdateLicenseV3 updates a new license v3 in db
func (r *Repo) UpdateLicenseV3(ctx context.Context, l *model.LicenseV3) error {
// the key and id for the license can't change so only update the data here!
query := `UPDATE licenses_v3 SET data=$1 WHERE id=$2;`
license, err := json.Marshal(l.Data)
if err != nil {
return fmt.Errorf("insert license failed: license marshal error")
}
_, err = r.db.ExecContext(ctx,
query,
license,
l.ID,
)
if err != nil {
zap.L().Error("error in updating license data: ", zap.Error(err))
return fmt.Errorf("failed to update license in db: %v", err)
}
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"time"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"sync"
@@ -46,12 +45,11 @@ type Manager struct {
failedAttempts uint64
// keep track of active license and features
activeLicense *model.License
activeLicenseV3 *model.LicenseV3
activeFeatures basemodel.FeatureSet
activeLicense *model.License
activeFeatures basemodel.FeatureSet
}
func StartManager(dbType string, db *sqlx.DB, useLicensesV3 bool, features ...basemodel.Feature) (*Manager, error) {
func StartManager(dbType string, db *sqlx.DB, features ...basemodel.Feature) (*Manager, error) {
if LM != nil {
return LM, nil
}
@@ -67,7 +65,7 @@ func StartManager(dbType string, db *sqlx.DB, useLicensesV3 bool, features ...ba
repo: &repo,
}
if err := m.start(useLicensesV3, features...); err != nil {
if err := m.start(features...); err != nil {
return m, err
}
LM = m
@@ -75,14 +73,8 @@ func StartManager(dbType string, db *sqlx.DB, useLicensesV3 bool, features ...ba
}
// start loads active license in memory and initiates validator
func (lm *Manager) start(useLicensesV3 bool, features ...basemodel.Feature) error {
var err error
if useLicensesV3 {
err = lm.LoadActiveLicenseV3(features...)
} else {
err = lm.LoadActiveLicense(features...)
}
func (lm *Manager) start(features ...basemodel.Feature) error {
err := lm.LoadActiveLicense(features...)
return err
}
@@ -116,31 +108,6 @@ func (lm *Manager) SetActive(l *model.License, features ...basemodel.Feature) {
go lm.Validator(context.Background())
}
}
func (lm *Manager) SetActiveV3(l *model.LicenseV3, features ...basemodel.Feature) {
lm.mutex.Lock()
defer lm.mutex.Unlock()
if l == nil {
return
}
lm.activeLicenseV3 = l
lm.activeFeatures = append(l.Features, features...)
// set default features
setDefaultFeatures(lm)
err := lm.InitFeatures(lm.activeFeatures)
if err != nil {
zap.L().Panic("Couldn't activate features", zap.Error(err))
}
if !lm.validatorRunning {
// we want to make sure only one validator runs,
// we already have lock() so good to go
lm.validatorRunning = true
go lm.ValidatorV3(context.Background())
}
}
func setDefaultFeatures(lm *Manager) {
@@ -170,28 +137,6 @@ func (lm *Manager) LoadActiveLicense(features ...basemodel.Feature) error {
return nil
}
func (lm *Manager) LoadActiveLicenseV3(features ...basemodel.Feature) error {
active, err := lm.repo.GetActiveLicenseV3(context.Background())
if err != nil {
return err
}
if active != nil {
lm.SetActiveV3(active, features...)
} else {
zap.L().Info("No active license found, defaulting to basic plan")
// if no active license is found, we default to basic(free) plan with all default features
lm.activeFeatures = model.BasicPlan
setDefaultFeatures(lm)
err := lm.InitFeatures(lm.activeFeatures)
if err != nil {
zap.L().Error("Couldn't initialize features", zap.Error(err))
return err
}
}
return nil
}
func (lm *Manager) GetLicenses(ctx context.Context) (response []model.License, apiError *model.ApiError) {
licenses, err := lm.repo.GetLicenses(ctx)
@@ -218,23 +163,6 @@ func (lm *Manager) GetLicenses(ctx context.Context) (response []model.License, a
return
}
func (lm *Manager) GetLicensesV3(ctx context.Context) (response []*model.LicenseV3, apiError *model.ApiError) {
licenses, err := lm.repo.GetLicensesV3(ctx)
if err != nil {
return nil, model.InternalError(err)
}
for _, l := range licenses {
if lm.activeLicenseV3 != nil && l.Key == lm.activeLicenseV3.Key {
l.IsCurrent = true
}
response = append(response, l)
}
return response, nil
}
// Validator validates license after an epoch of time
func (lm *Manager) Validator(ctx context.Context) {
defer close(lm.terminated)
@@ -259,30 +187,6 @@ func (lm *Manager) Validator(ctx context.Context) {
}
}
// Validator validates license after an epoch of time
func (lm *Manager) ValidatorV3(ctx context.Context) {
defer close(lm.terminated)
tick := time.NewTicker(validationFrequency)
defer tick.Stop()
lm.ValidateV3(ctx)
for {
select {
case <-lm.done:
return
default:
select {
case <-lm.done:
return
case <-tick.C:
lm.ValidateV3(ctx)
}
}
}
}
// Validate validates the current active license
func (lm *Manager) Validate(ctx context.Context) (reterr error) {
zap.L().Info("License validation started")
@@ -350,54 +254,6 @@ func (lm *Manager) Validate(ctx context.Context) (reterr error) {
return nil
}
// todo[vikrantgupta25]: check the comparison here between old and new license!
func (lm *Manager) RefreshLicense(ctx context.Context) *model.ApiError {
license, apiError := validate.ValidateLicenseV3(lm.activeLicenseV3.Key)
if apiError != nil {
zap.L().Error("failed to validate license", zap.Error(apiError.Err))
return apiError
}
err := lm.repo.UpdateLicenseV3(ctx, license)
if err != nil {
return model.BadRequest(errors.Wrap(err, "failed to update the new license"))
}
lm.SetActiveV3(license)
return nil
}
func (lm *Manager) ValidateV3(ctx context.Context) (reterr error) {
zap.L().Info("License validation started")
if lm.activeLicenseV3 == nil {
return nil
}
defer func() {
lm.mutex.Lock()
lm.lastValidated = time.Now().Unix()
if reterr != nil {
zap.L().Error("License validation completed with error", zap.Error(reterr))
atomic.AddUint64(&lm.failedAttempts, 1)
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_CHECK_FAILED,
map[string]interface{}{"err": reterr.Error()}, "", true, false)
} else {
zap.L().Info("License validation completed with no errors")
}
lm.mutex.Unlock()
}()
err := lm.RefreshLicense(ctx)
if err != nil {
return err
}
return nil
}
// Activate activates a license key with signoz server
func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *model.License, errResponse *model.ApiError) {
defer func() {
@@ -442,35 +298,6 @@ func (lm *Manager) Activate(ctx context.Context, key string) (licenseResponse *m
return l, nil
}
func (lm *Manager) ActivateV3(ctx context.Context, licenseKey string) (licenseResponse *model.LicenseV3, errResponse *model.ApiError) {
defer func() {
if errResponse != nil {
userEmail, err := auth.GetEmailFromJwt(ctx)
if err == nil {
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_ACT_FAILED,
map[string]interface{}{"err": errResponse.Err.Error()}, userEmail, true, false)
}
}
}()
license, apiError := validate.ValidateLicenseV3(licenseKey)
if apiError != nil {
zap.L().Error("failed to get the license", zap.Error(apiError.Err))
return nil, apiError
}
// insert the new license to the sqlite db
err := lm.repo.InsertLicenseV3(ctx, license)
if err != nil {
zap.L().Error("failed to activate license", zap.Error(err))
return nil, model.InternalError(err)
}
// license is valid, activate it
lm.SetActiveV3(license)
return license, nil
}
// CheckFeature will be internally used by backend routines
// for feature gating
func (lm *Manager) CheckFeature(featureKey string) error {

View File

@@ -48,16 +48,5 @@ func InitDB(db *sqlx.DB) error {
return fmt.Errorf("error in creating feature_status table: %s", err.Error())
}
table_schema = `CREATE TABLE IF NOT EXISTS licenses_v3 (
id TEXT PRIMARY KEY,
key TEXT NOT NULL UNIQUE,
data TEXT
);`
_, err = db.Exec(table_schema)
if err != nil {
return fmt.Errorf("error in creating licenses_v3 table: %s", err.Error())
}
return nil
}

View File

@@ -16,7 +16,6 @@ import (
"go.signoz.io/signoz/pkg/query-service/auth"
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/migrate"
"go.signoz.io/signoz/pkg/query-service/version"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@@ -94,7 +93,6 @@ func main() {
var cluster string
var useLogsNewSchema bool
var useLicensesV3 bool
var cacheConfigPath, fluxInterval string
var enableQueryServiceLogOTLPExport bool
var preferSpanMetrics bool
@@ -105,7 +103,6 @@ func main() {
var gatewayUrl string
flag.BoolVar(&useLogsNewSchema, "use-logs-new-schema", false, "use logs_v2 schema for logs")
flag.BoolVar(&useLicensesV3, "use-licenses-v3", false, "use licenses_v3 schema for licenses")
flag.StringVar(&promConfigPath, "config", "./config/prometheus.yml", "(prometheus config to read metrics)")
flag.StringVar(&skipTopLvlOpsPath, "skip-top-level-ops", "", "(config file to skip top level operations)")
flag.BoolVar(&disableRules, "rules.disable", false, "(disable rule evaluation)")
@@ -127,8 +124,6 @@ func main() {
zap.ReplaceGlobals(loggerMgr)
defer loggerMgr.Sync() // flushes buffer, if any
version.PrintVersion()
serverOptions := &app.ServerOptions{
HTTPHostPort: baseconst.HTTPHostPort,
PromConfigPath: promConfigPath,
@@ -145,7 +140,6 @@ func main() {
Cluster: cluster,
GatewayUrl: gatewayUrl,
UseLogsNewSchema: useLogsNewSchema,
UseLicensesV3: useLicensesV3,
}
// Read the jwt secret key
@@ -181,8 +175,6 @@ func main() {
for {
select {
case status := <-server.HealthCheckStatus():
zap.L().Info("Received HealthCheck status: ", zap.Int("status", int(status)))
case <-signalsChannel:
zap.L().Fatal("Received OS Interrupt Signal ... ")
server.Stop()

View File

@@ -46,13 +46,6 @@ func BadRequest(err error) *ApiError {
}
}
func Unauthorized(err error) *ApiError {
return &ApiError{
Typ: basemodel.ErrorUnauthorized,
Err: err,
}
}
// BadRequestStr returns a ApiError object of bad request for string input
func BadRequestStr(s string) *ApiError {
return &ApiError{

View File

@@ -3,8 +3,6 @@ package model
import (
"encoding/base64"
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/pkg/errors"
@@ -106,144 +104,3 @@ type SubscriptionServerResp struct {
Status string `json:"status"`
Data Licenses `json:"data"`
}
type Plan struct {
Name string `json:"name"`
}
type LicenseDB struct {
ID string `json:"id"`
Key string `json:"key"`
Data string `json:"data"`
}
type LicenseV3 struct {
ID string
Key string
Data map[string]interface{}
PlanName string
Features basemodel.FeatureSet
Status string
IsCurrent bool
ValidFrom int64
ValidUntil int64
}
func extractKeyFromMapStringInterface[T any](data map[string]interface{}, key string) (T, error) {
var zeroValue T
if val, ok := data[key]; ok {
if value, ok := val.(T); ok {
return value, nil
}
return zeroValue, fmt.Errorf("%s key is not a valid %s", key, reflect.TypeOf(zeroValue))
}
return zeroValue, fmt.Errorf("%s key is missing", key)
}
func NewLicenseV3(data map[string]interface{}) (*LicenseV3, error) {
var features basemodel.FeatureSet
// extract id from data
licenseID, err := extractKeyFromMapStringInterface[string](data, "id")
if err != nil {
return nil, err
}
delete(data, "id")
// extract key from data
licenseKey, err := extractKeyFromMapStringInterface[string](data, "key")
if err != nil {
return nil, err
}
delete(data, "key")
// extract status from data
status, err := extractKeyFromMapStringInterface[string](data, "status")
if err != nil {
return nil, err
}
planMap, err := extractKeyFromMapStringInterface[map[string]any](data, "plan")
if err != nil {
return nil, err
}
planName, err := extractKeyFromMapStringInterface[string](planMap, "name")
if err != nil {
return nil, err
}
// if license status is inactive then default it to basic
if status == LicenseStatusInactive {
planName = PlanNameBasic
}
featuresFromZeus := basemodel.FeatureSet{}
if _features, ok := data["features"]; ok {
featuresData, err := json.Marshal(_features)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal features data")
}
if err := json.Unmarshal(featuresData, &featuresFromZeus); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal features data")
}
}
switch planName {
case PlanNameTeams:
features = append(features, ProPlan...)
case PlanNameEnterprise:
features = append(features, EnterprisePlan...)
case PlanNameBasic:
features = append(features, BasicPlan...)
default:
features = append(features, BasicPlan...)
}
if len(featuresFromZeus) > 0 {
for _, feature := range featuresFromZeus {
exists := false
for i, existingFeature := range features {
if existingFeature.Name == feature.Name {
features[i] = feature // Replace existing feature
exists = true
break
}
}
if !exists {
features = append(features, feature) // Append if it doesn't exist
}
}
}
data["features"] = features
_validFrom, err := extractKeyFromMapStringInterface[float64](data, "valid_from")
if err != nil {
_validFrom = 0
}
validFrom := int64(_validFrom)
_validUntil, err := extractKeyFromMapStringInterface[float64](data, "valid_until")
if err != nil {
_validUntil = 0
}
validUntil := int64(_validUntil)
return &LicenseV3{
ID: licenseID,
Key: licenseKey,
Data: data,
PlanName: planName,
Features: features,
ValidFrom: validFrom,
ValidUntil: validUntil,
Status: status,
}, nil
}
func NewLicenseV3WithIDAndKey(id string, key string, data map[string]interface{}) (*LicenseV3, error) {
licenseDataWithIdAndKey := data
licenseDataWithIdAndKey["id"] = id
licenseDataWithIdAndKey["key"] = key
return NewLicenseV3(licenseDataWithIdAndKey)
}

View File

@@ -1,170 +0,0 @@
package model
import (
"encoding/json"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.signoz.io/signoz/pkg/query-service/model"
)
func TestNewLicenseV3(t *testing.T) {
testCases := []struct {
name string
data []byte
pass bool
expected *LicenseV3
error error
}{
{
name: "Error for missing license id",
data: []byte(`{}`),
pass: false,
error: errors.New("id key is missing"),
},
{
name: "Error for license id not being a valid string",
data: []byte(`{"id": 10}`),
pass: false,
error: errors.New("id key is not a valid string"),
},
{
name: "Error for missing license key",
data: []byte(`{"id":"does-not-matter"}`),
pass: false,
error: errors.New("key key is missing"),
},
{
name: "Error for invalid string license key",
data: []byte(`{"id":"does-not-matter","key":10}`),
pass: false,
error: errors.New("key key is not a valid string"),
},
{
name: "Error for missing license status",
data: []byte(`{"id":"does-not-matter", "key": "does-not-matter","category":"FREE"}`),
pass: false,
error: errors.New("status key is missing"),
},
{
name: "Error for invalid string license status",
data: []byte(`{"id":"does-not-matter","key": "does-not-matter", "category":"FREE", "status":10}`),
pass: false,
error: errors.New("status key is not a valid string"),
},
{
name: "Error for missing license plan",
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE"}`),
pass: false,
error: errors.New("plan key is missing"),
},
{
name: "Error for invalid json license plan",
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":10}`),
pass: false,
error: errors.New("plan key is not a valid map[string]interface {}"),
},
{
name: "Error for invalid license plan",
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{}}`),
pass: false,
error: errors.New("name key is missing"),
},
{
name: "Parse the entire license properly",
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{"name":"TEAMS"},"valid_from": 1730899309,"valid_until": -1}`),
pass: true,
expected: &LicenseV3{
ID: "does-not-matter",
Key: "does-not-matter-key",
Data: map[string]interface{}{
"plan": map[string]interface{}{
"name": "TEAMS",
},
"category": "FREE",
"status": "ACTIVE",
"valid_from": float64(1730899309),
"valid_until": float64(-1),
},
PlanName: PlanNameTeams,
ValidFrom: 1730899309,
ValidUntil: -1,
Status: "ACTIVE",
IsCurrent: false,
Features: model.FeatureSet{},
},
},
{
name: "Fallback to basic plan if license status is inactive",
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"INACTIVE","plan":{"name":"TEAMS"},"valid_from": 1730899309,"valid_until": -1}`),
pass: true,
expected: &LicenseV3{
ID: "does-not-matter",
Key: "does-not-matter-key",
Data: map[string]interface{}{
"plan": map[string]interface{}{
"name": "TEAMS",
},
"category": "FREE",
"status": "INACTIVE",
"valid_from": float64(1730899309),
"valid_until": float64(-1),
},
PlanName: PlanNameBasic,
ValidFrom: 1730899309,
ValidUntil: -1,
Status: "INACTIVE",
IsCurrent: false,
Features: model.FeatureSet{},
},
},
{
name: "fallback states for validFrom and validUntil",
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{"name":"TEAMS"},"valid_from":1234.456,"valid_until":5678.567}`),
pass: true,
expected: &LicenseV3{
ID: "does-not-matter",
Key: "does-not-matter-key",
Data: map[string]interface{}{
"plan": map[string]interface{}{
"name": "TEAMS",
},
"valid_from": 1234.456,
"valid_until": 5678.567,
"category": "FREE",
"status": "ACTIVE",
},
PlanName: PlanNameTeams,
ValidFrom: 1234,
ValidUntil: 5678,
Status: "ACTIVE",
IsCurrent: false,
Features: model.FeatureSet{},
},
},
}
for _, tc := range testCases {
var licensePayload map[string]interface{}
err := json.Unmarshal(tc.data, &licensePayload)
require.NoError(t, err)
license, err := NewLicenseV3(licensePayload)
if license != nil {
license.Features = make(model.FeatureSet, 0)
delete(license.Data, "features")
}
if tc.pass {
require.NoError(t, err)
require.NotNil(t, license)
assert.Equal(t, tc.expected, license)
} else {
require.Error(t, err)
assert.EqualError(t, err, tc.error.Error())
require.Nil(t, license)
}
}
}

View File

@@ -9,17 +9,6 @@ const SSO = "SSO"
const Basic = "BASIC_PLAN"
const Pro = "PRO_PLAN"
const Enterprise = "ENTERPRISE_PLAN"
var (
PlanNameEnterprise = "ENTERPRISE"
PlanNameTeams = "TEAMS"
PlanNameBasic = "BASIC"
)
var (
LicenseStatusInactive = "INACTIVE"
)
const DisableUpsell = "DISABLE_UPSELL"
const Onboarding = "ONBOARDING"
const ChatSupport = "CHAT_SUPPORT"

View File

@@ -1,15 +1,10 @@
package rules
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
baserules "go.signoz.io/signoz/pkg/query-service/rules"
"go.signoz.io/signoz/pkg/query-service/utils/labels"
"go.uber.org/zap"
)
func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) {
@@ -84,106 +79,6 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
return task, nil
}
// TestNotification prepares a dummy rule for given rule parameters and
// sends a test notification. returns alert count and error (if any)
func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.ApiError) {
ctx := context.Background()
if opts.Rule == nil {
return 0, basemodel.BadRequest(fmt.Errorf("rule is required"))
}
parsedRule := opts.Rule
var alertname = parsedRule.AlertName
if alertname == "" {
// alertname is not mandatory for testing, so picking
// a random string here
alertname = uuid.New().String()
}
// append name to indicate this is test alert
parsedRule.AlertName = fmt.Sprintf("%s%s", alertname, baserules.TestAlertPostFix)
var rule baserules.Rule
var err error
if parsedRule.RuleType == baserules.RuleTypeThreshold {
// add special labels for test alerts
parsedRule.Annotations[labels.AlertSummaryLabel] = fmt.Sprintf("The rule threshold is set to %.4f, and the observed metric value is {{$value}}.", *parsedRule.RuleCondition.Target)
parsedRule.Labels[labels.RuleSourceLabel] = ""
parsedRule.Labels[labels.AlertRuleIdLabel] = ""
// create a threshold rule
rule, err = baserules.NewThresholdRule(
alertname,
parsedRule,
opts.FF,
opts.Reader,
opts.UseLogsNewSchema,
baserules.WithSendAlways(),
baserules.WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new threshold rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, basemodel.BadRequest(err)
}
} else if parsedRule.RuleType == baserules.RuleTypeProm {
// create promql rule
rule, err = baserules.NewPromRule(
alertname,
parsedRule,
opts.Logger,
opts.Reader,
opts.ManagerOpts.PqlEngine,
baserules.WithSendAlways(),
baserules.WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new promql rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, basemodel.BadRequest(err)
}
} else if parsedRule.RuleType == baserules.RuleTypeAnomaly {
// create anomaly rule
rule, err = NewAnomalyRule(
alertname,
parsedRule,
opts.FF,
opts.Reader,
opts.Cache,
baserules.WithSendAlways(),
baserules.WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new anomaly rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, basemodel.BadRequest(err)
}
} else {
return 0, basemodel.BadRequest(fmt.Errorf("failed to derive ruletype with given information"))
}
// set timestamp to current utc time
ts := time.Now().UTC()
count, err := rule.Eval(ctx, ts)
if err != nil {
zap.L().Error("evaluating rule failed", zap.String("rule", rule.Name()), zap.Error(err))
return 0, basemodel.InternalError(fmt.Errorf("rule evaluation failed"))
}
alertsFound, ok := count.(int)
if !ok {
return 0, basemodel.InternalError(fmt.Errorf("something went wrong"))
}
rule.SendAlerts(ctx, ts, 0, time.Duration(1*time.Minute), opts.NotifyFunc)
return alertsFound, nil
}
// newTask returns an appropriate group for
// rule type
func newTask(taskType baserules.TaskType, name string, frequency time.Duration, rules []baserules.Rule, opts *baserules.ManagerOptions, notify baserules.NotifyFunc, ruleDB baserules.RuleDB) baserules.Task {

View File

@@ -48,6 +48,7 @@ type Manager struct {
func New(dbType string, modelDao dao.ModelDao, licenseRepo *license.Repo, clickhouseConn clickhouse.Conn) (*Manager, error) {
hostNameRegex := regexp.MustCompile(`tcp://(?P<hostname>.*):`)
// todo(remove): read from config
hostNameRegexMatches := hostNameRegex.FindStringSubmatch(os.Getenv("ClickHouseUrl"))
tenantID := ""

View File

@@ -42,7 +42,7 @@
"@radix-ui/react-tooltip": "1.0.7",
"@sentry/react": "7.102.1",
"@sentry/webpack-plugin": "2.16.0",
"@signozhq/design-tokens": "1.1.4",
"@signozhq/design-tokens": "0.0.8",
"@uiw/react-md-editor": "3.23.5",
"@visx/group": "3.3.0",
"@visx/shape": "3.5.0",

View File

@@ -1,24 +0,0 @@
{
"metricGraphCategory": {
"brokerMetrics": {
"title": "Broker Metrics",
"description": "The Kafka Broker metrics here inform you of data loss/delay through unclean leader elections and network throughputs, as well as request fails through request purgatories and timeouts metrics"
},
"consumerMetrics": {
"title": "Consumer Metrics",
"description": "Kafka Consumer metrics provide insights into lag between message production and consumption, success rates and latency of message delivery, and the volume of data consumed."
},
"producerMetrics": {
"title": "Producer Metrics",
"description": "Kafka Producers send messages to brokers for storage and distribution by topic. These metrics inform you of the volume and rate of data sent, and the success rate of message delivery."
},
"brokerJVMMetrics": {
"title": "Broker JVM Metrics",
"description": "Kafka brokers are Java applications that expose JVM metrics to inform on the broker's system health. Garbage collection metrics like those below provide key insights into free memory, broker performance, and heap size. You need to enable new_gc_metrics for this section to populate."
},
"partitionMetrics": {
"title": "Partition Metrics",
"description": "Kafka partitions are the unit of parallelism in Kafka. These metrics inform you of the number of partitions per topic, the current offset of each partition, the oldest offset, and the number of in-sync replicas."
}
}
}

View File

@@ -1,54 +1,30 @@
{
"breadcrumb": "Messaging Queues",
"header": "Kafka / Overview",
"overview": {
"title": "Start sending data in as little as 20 minutes",
"subtitle": "Connect and Monitor Your Data Streams"
},
"configureConsumer": {
"title": "Configure Consumer",
"description": "Add consumer data sources to gain insights and enhance monitoring.",
"button": "Get Started"
},
"configureProducer": {
"title": "Configure Producer",
"description": "Add producer data sources to gain insights and enhance monitoring.",
"button": "Get Started"
},
"monitorKafka": {
"title": "Monitor kafka",
"description": "Add your Kafka source to gain insights and enhance activity tracking.",
"button": "Get Started"
},
"summarySection": {
"viewDetailsButton": "View Details",
"consumer": {
"title": "Consumer lag view",
"description": "Connect and Monitor Your Data Streams"
},
"producer": {
"title": "Producer latency view",
"description": "Connect and Monitor Your Data Streams"
},
"partition": {
"title": "Partition Latency view",
"description": "Connect and Monitor Your Data Streams"
},
"dropRate": {
"title": "Drop Rate view",
"description": "Connect and Monitor Your Data Streams"
},
"metricPage": {
"title": "Metric View",
"description": "Connect and Monitor Your Data Streams"
}
},
"confirmModal": {
"content": "Before navigating to the details page, please make sure you have configured all the required setup to ensure correct data monitoring.",
"okText": "Proceed"
},
"overviewSummarySection": {
"title": "Monitor Your Data Streams",
"subtitle": "Monitor key Kafka metrics like consumer lag and latency to ensure efficient data flow and troubleshoot in real time."
}
}
"breadcrumb": "Messaging Queues",
"header": "Kafka / Overview",
"overview": {
"title": "Start sending data in as little as 20 minutes",
"subtitle": "Connect and Monitor Your Data Streams"
},
"configureConsumer": {
"title": "Configure Consumer",
"description": "Add consumer data sources to gain insights and enhance monitoring.",
"button": "Get Started"
},
"configureProducer": {
"title": "Configure Producer",
"description": "Add producer data sources to gain insights and enhance monitoring.",
"button": "Get Started"
},
"monitorKafka": {
"title": "Monitor kafka",
"description": "Add your Kafka source to gain insights and enhance activity tracking.",
"button": "Get Started"
},
"summarySection": {
"viewDetailsButton": "View Details"
},
"confirmModal": {
"content": "Before navigating to the details page, please make sure you have configured all the required setup to ensure correct data monitoring.",
"okText": "Proceed"
}
}

View File

@@ -1,24 +0,0 @@
{
"metricGraphCategory": {
"brokerMetrics": {
"title": "Broker Metrics",
"description": "The Kafka Broker metrics here inform you of data loss/delay through unclean leader elections and network throughputs, as well as request fails through request purgatories and timeouts metrics"
},
"consumerMetrics": {
"title": "Consumer Metrics",
"description": "Kafka Consumer metrics provide insights into lag between message production and consumption, success rates and latency of message delivery, and the volume of data consumed."
},
"producerMetrics": {
"title": "Producer Metrics",
"description": "Kafka Producers send messages to brokers for storage and distribution by topic. These metrics inform you of the volume and rate of data sent, and the success rate of message delivery."
},
"brokerJVMMetrics": {
"title": "Broker JVM Metrics",
"description": "Kafka brokers are Java applications that expose JVM metrics to inform on the broker's system health. Garbage collection metrics like those below provide key insights into free memory, broker performance, and heap size. You need to enable new_gc_metrics for this section to populate."
},
"partitionMetrics": {
"title": "Partition Metrics",
"description": "Kafka partitions are the unit of parallelism in Kafka. These metrics inform you of the number of partitions per topic, the current offset of each partition, the oldest offset, and the number of in-sync replicas."
}
}
}

View File

@@ -37,10 +37,6 @@
"dropRate": {
"title": "Drop Rate view",
"description": "Connect and Monitor Your Data Streams"
},
"metricPage": {
"title": "Metric View",
"description": "Connect and Monitor Your Data Streams"
}
},
"confirmModal": {

View File

@@ -18,5 +18,4 @@ export const REACT_QUERY_KEY = {
GET_ALL_ALLERTS: 'GET_ALL_ALLERTS',
REMOVE_ALERT_RULE: 'REMOVE_ALERT_RULE',
DUPLICATE_ALERT_RULE: 'DUPLICATE_ALERT_RULE',
UPDATE_ALERT_RULE: 'UPDATE_ALERT_RULE',
};

View File

@@ -57,7 +57,6 @@ export const alertDefaults: AlertDef = {
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
alert: '',
};
export const anamolyAlertDefaults: AlertDef = {
@@ -102,7 +101,6 @@ export const anamolyAlertDefaults: AlertDef = {
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
alert: '',
};
export const logAlertDefaults: AlertDef = {
@@ -134,7 +132,6 @@ export const logAlertDefaults: AlertDef = {
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
alert: '',
};
export const traceAlertDefaults: AlertDef = {
@@ -166,7 +163,6 @@ export const traceAlertDefaults: AlertDef = {
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
alert: '',
};
export const exceptionAlertDefaults: AlertDef = {
@@ -198,7 +194,6 @@ export const exceptionAlertDefaults: AlertDef = {
},
annotations: defaultAnnotations,
evalWindow: defaultEvalWindow,
alert: '',
};
export const ALERTS_VALUES_MAP: Record<AlertTypes, AlertDef> = {

View File

@@ -1,4 +1,4 @@
import { Color, ColorType } from '@signozhq/design-tokens';
import { Color } from '@signozhq/design-tokens';
import { showErrorNotification } from 'components/ExplorerCard/utils';
import { LOCALSTORAGE } from 'constants/localStorage';
import { QueryParams } from 'constants/query';
@@ -8,7 +8,7 @@ import { DataSource } from 'types/common/queryBuilder';
import { SaveNewViewHandlerProps } from './types';
export const getRandomColor = (): ColorType => {
export const getRandomColor = (): Color => {
const colorKeys = Object.keys(Color) as (keyof typeof Color)[];
const randomKey = colorKeys[Math.floor(Math.random() * colorKeys.length)];
return Color[randomKey];

View File

@@ -53,7 +53,6 @@ import {
QueryFunctionProps,
} from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
import BasicInfo from './BasicInfo';
@@ -106,11 +105,6 @@ function FormAlertRules({
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const dataSource = useMemo(
() => urlQuery.get(QueryParams.alertType) as DataSource,
[urlQuery],
);
// In case of alert the panel types should always be "Graph" only
const panelType = PANEL_TYPES.TIME_SERIES;
@@ -120,12 +114,13 @@ function FormAlertRules({
handleSetQueryData,
handleRunQuery,
handleSetConfig,
initialDataSource,
redirectWithQueryBuilderData,
} = useQueryBuilder();
useEffect(() => {
handleSetConfig(panelType || PANEL_TYPES.TIME_SERIES, dataSource);
}, [handleSetConfig, dataSource, panelType]);
handleSetConfig(panelType || PANEL_TYPES.TIME_SERIES, initialDataSource);
}, [handleSetConfig, initialDataSource, panelType]);
// use query client
const ruleCache = useQueryClient();

View File

@@ -2,90 +2,82 @@ import './ActionButtons.styles.scss';
import { Color } from '@signozhq/design-tokens';
import { Divider, Dropdown, MenuProps, Switch, Tooltip } from 'antd';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history';
import { Copy, Ellipsis, PenLine, Trash2 } from 'lucide-react';
import {
useAlertRuleDelete,
useAlertRuleDuplicate,
useAlertRuleStatusToggle,
useAlertRuleUpdate,
} from 'pages/AlertDetails/hooks';
import CopyToClipboard from 'periscope/components/CopyToClipboard';
import { useAlertRule } from 'providers/Alert';
import { useCallback, useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { CSSProperties } from 'styled-components';
import { AlertDef } from 'types/api/alerts/def';
import { AlertHeaderProps } from '../AlertHeader';
import RenameModal from './RenameModal';
const menuItemStyle: CSSProperties = {
fontSize: '14px',
letterSpacing: '0.14px',
};
function AlertActionButtons({
ruleId,
alertDetails,
setUpdatedName,
}: {
ruleId: string;
alertDetails: AlertHeaderProps['alertDetails'];
setUpdatedName: (name: string) => void;
}): JSX.Element {
const { alertRuleState, setAlertRuleState } = useAlertRule();
const [intermediateName, setIntermediateName] = useState<string>(
alertDetails.alert,
);
const [isRenameAlertOpen, setIsRenameAlertOpen] = useState<boolean>(false);
const isDarkMode = useIsDarkMode();
const { handleAlertStateToggle } = useAlertRuleStatusToggle({ ruleId });
const { handleAlertDuplicate } = useAlertRuleDuplicate({
alertDetails: (alertDetails as unknown) as AlertDef,
});
const { handleAlertDelete } = useAlertRuleDelete({ ruleId: Number(ruleId) });
const { handleAlertUpdate, isLoading } = useAlertRuleUpdate({
alertDetails: (alertDetails as unknown) as AlertDef,
setUpdatedName,
intermediateName,
});
const handleRename = useCallback(() => {
setIsRenameAlertOpen(true);
}, []);
const params = useUrlQuery();
const onNameChangeHandler = useCallback(() => {
handleAlertUpdate();
setIsRenameAlertOpen(false);
}, [handleAlertUpdate]);
const handleRename = React.useCallback(() => {
params.set(QueryParams.ruleId, String(ruleId));
history.push(`${ROUTES.ALERT_OVERVIEW}?${params.toString()}`);
}, [params, ruleId]);
const menuItems: MenuProps['items'] = [
{
key: 'rename-rule',
label: 'Rename',
icon: <PenLine size={16} color={Color.BG_VANILLA_400} />,
onClick: handleRename,
style: menuItemStyle,
},
{
key: 'duplicate-rule',
label: 'Duplicate',
icon: <Copy size={16} color={Color.BG_VANILLA_400} />,
onClick: handleAlertDuplicate,
style: menuItemStyle,
},
{
key: 'delete-rule',
label: 'Delete',
icon: <Trash2 size={16} color={Color.BG_CHERRY_400} />,
onClick: handleAlertDelete,
style: {
...menuItemStyle,
color: Color.BG_CHERRY_400,
const menu: MenuProps['items'] = React.useMemo(
() => [
{
key: 'rename-rule',
label: 'Rename',
icon: <PenLine size={16} color={Color.BG_VANILLA_400} />,
onClick: (): void => handleRename(),
style: menuItemStyle,
},
},
];
{
key: 'duplicate-rule',
label: 'Duplicate',
icon: <Copy size={16} color={Color.BG_VANILLA_400} />,
onClick: (): void => handleAlertDuplicate(),
style: menuItemStyle,
},
{ type: 'divider' },
{
key: 'delete-rule',
label: 'Delete',
icon: <Trash2 size={16} color={Color.BG_CHERRY_400} />,
onClick: (): void => handleAlertDelete(),
style: {
...menuItemStyle,
color: Color.BG_CHERRY_400,
},
},
],
[handleAlertDelete, handleAlertDuplicate, handleRename],
);
const isDarkMode = useIsDarkMode();
// state for immediate UI feedback rather than waiting for onSuccess of handleAlertStateTiggle to updating the alertRuleState
const [isAlertRuleDisabled, setIsAlertRuleDisabled] = useState<
@@ -103,48 +95,35 @@ function AlertActionButtons({
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => (): void => setAlertRuleState(undefined), []);
const toggleAlertRule = useCallback(() => {
setIsAlertRuleDisabled((prev) => !prev);
handleAlertStateToggle();
}, [handleAlertStateToggle]);
return (
<>
<div className="alert-action-buttons">
<Tooltip title={alertRuleState ? 'Enable alert' : 'Disable alert'}>
{isAlertRuleDisabled !== undefined && (
<Switch
size="small"
onChange={toggleAlertRule}
checked={!isAlertRuleDisabled}
/>
)}
<div className="alert-action-buttons">
<Tooltip title={alertRuleState ? 'Enable alert' : 'Disable alert'}>
{isAlertRuleDisabled !== undefined && (
<Switch
size="small"
onChange={(): void => {
setIsAlertRuleDisabled((prev) => !prev);
handleAlertStateToggle();
}}
checked={!isAlertRuleDisabled}
/>
)}
</Tooltip>
<CopyToClipboard textToCopy={window.location.href} />
<Divider type="vertical" />
<Dropdown trigger={['click']} menu={{ items: menu }}>
<Tooltip title="More options">
<Ellipsis
size={16}
color={isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400}
cursor="pointer"
className="dropdown-icon"
/>
</Tooltip>
<CopyToClipboard textToCopy={window.location.href} />
<Divider type="vertical" />
<Dropdown trigger={['click']} menu={{ items: menuItems }}>
<Tooltip title="More options">
<Ellipsis
size={16}
color={isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400}
cursor="pointer"
className="dropdown-icon"
/>
</Tooltip>
</Dropdown>
</div>
<RenameModal
isOpen={isRenameAlertOpen}
setIsOpen={setIsRenameAlertOpen}
isLoading={isLoading}
onNameChangeHandler={onNameChangeHandler}
intermediateName={intermediateName}
setIntermediateName={setIntermediateName}
/>
</>
</Dropdown>
</div>
);
}

View File

@@ -1,138 +0,0 @@
.rename-alert {
.ant-modal-content {
width: 384px;
flex-shrink: 0;
border-radius: 4px;
border: 1px solid var(--bg-slate-500);
background: var(--bg-ink-400);
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
padding: 0px;
.ant-modal-header {
height: 52px;
padding: 16px;
background: var(--bg-ink-400);
border-bottom: 1px solid var(--bg-slate-500);
margin-bottom: 0px;
.ant-modal-title {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
width: 349px;
height: 20px;
}
}
.ant-modal-body {
padding: 16px;
.alert-content {
display: flex;
flex-direction: column;
gap: 8px;
.name-text {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
}
.alert-name-input {
display: flex;
padding: 6px 6px 6px 8px;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
}
}
}
.ant-modal-footer {
padding: 16px;
margin-top: 0px;
.alert-rename {
display: flex;
flex-direction: row-reverse;
gap: 12px;
.cancel-btn {
display: flex;
padding: 4px 8px;
justify-content: center;
align-items: center;
gap: 4px;
border-radius: 2px;
background: var(--bg-slate-500);
.ant-btn-icon {
margin-inline-end: 0px;
}
}
.rename-btn {
display: flex;
align-items: center;
display: flex;
padding: 4px 8px;
justify-content: center;
align-items: center;
gap: 4px;
border-radius: 2px;
background: var(--bg-robin-500);
.ant-btn-icon {
margin-inline-end: 0px;
}
}
}
}
}
}
.lightMode {
.rename-alert {
.ant-modal-content {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
.ant-modal-header {
background: var(--bg-vanilla-100);
border-bottom: 1px solid var(--bg-vanilla-300);
.ant-modal-title {
color: var(--bg-ink-300);
}
}
.ant-modal-body {
.alert-content {
.name-text {
color: var(--bg-ink-300);
}
.alert-name-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
}
}
}
.ant-modal-footer {
.alert-rename {
.cancel-btn {
background: var(--bg-vanilla-300);
}
}
}
}
}
}

View File

@@ -1,95 +0,0 @@
import './RenameModal.styles.scss';
import { Button, Input, InputRef, Modal, Typography } from 'antd';
import { Check, X } from 'lucide-react';
import { useCallback, useEffect, useRef } from 'react';
type Props = {
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
onNameChangeHandler: () => void;
isLoading: boolean;
intermediateName: string;
setIntermediateName: (name: string) => void;
};
function RenameModal({
isOpen,
setIsOpen,
onNameChangeHandler,
isLoading,
intermediateName,
setIntermediateName,
}: Props): JSX.Element {
const inputRef = useRef<InputRef>(null);
useEffect(() => {
if (isOpen && inputRef.current) {
inputRef.current.focus();
}
}, [isOpen]);
const handleClose = useCallback((): void => setIsOpen(false), [setIsOpen]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent): void => {
if (isOpen) {
if (e.key === 'Enter') {
onNameChangeHandler();
} else if (e.key === 'Escape') {
handleClose();
}
}
};
document.addEventListener('keydown', handleKeyDown);
return (): void => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, onNameChangeHandler, handleClose]);
return (
<Modal
open={isOpen}
title="Rename Alert"
onOk={onNameChangeHandler}
onCancel={handleClose}
rootClassName="rename-alert"
footer={
<div className="alert-rename">
<Button
type="primary"
icon={<Check size={14} />}
className="rename-btn"
onClick={onNameChangeHandler}
disabled={isLoading}
>
Rename Alert
</Button>
<Button
type="text"
icon={<X size={14} />}
className="cancel-btn"
onClick={handleClose}
>
Cancel
</Button>
</div>
}
>
<div className="alert-content">
<Typography.Text className="name-text">Enter a new name</Typography.Text>
<Input
ref={inputRef}
data-testid="alert-name"
className="alert-name-input"
value={intermediateName}
onChange={(e): void => setIntermediateName(e.target.value)}
/>
</div>
</Modal>
);
}
export default RenameModal;

View File

@@ -2,7 +2,7 @@ import './AlertHeader.styles.scss';
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
import { useAlertRule } from 'providers/Alert';
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import AlertActionButtons from './ActionButtons/ActionButtons';
import AlertLabels from './AlertLabels/AlertLabels';
@@ -19,9 +19,7 @@ export type AlertHeaderProps = {
};
};
function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
const { state, alert: alertName, labels } = alertDetails;
const { alertRuleState } = useAlertRule();
const [updatedName, setUpdatedName] = useState(alertName);
const { state, alert, labels } = alertDetails;
const labelsWithoutSeverity = useMemo(
() =>
@@ -31,6 +29,8 @@ function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
[labels],
);
const { alertRuleState } = useAlertRule();
return (
<div className="alert-info">
<div className="alert-info__info-wrapper">
@@ -38,7 +38,7 @@ function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
<div className="alert-title-wrapper">
<AlertState state={alertRuleState ?? state} />
<div className="alert-title">
<LineClampedText text={updatedName || alertName} />
<LineClampedText text={alert} />
</div>
</div>
</div>
@@ -54,11 +54,7 @@ function AlertHeader({ alertDetails }: AlertHeaderProps): JSX.Element {
</div>
</div>
<div className="alert-info__action-buttons">
<AlertActionButtons
alertDetails={alertDetails}
ruleId={alertDetails.id}
setUpdatedName={setUpdatedName}
/>
<AlertActionButtons alertDetails={alertDetails} ruleId={alertDetails.id} />
</div>
</div>
);

View File

@@ -467,44 +467,6 @@ export const useAlertRuleDuplicate = ({
return { handleAlertDuplicate };
};
export const useAlertRuleUpdate = ({
alertDetails,
setUpdatedName,
intermediateName,
}: {
alertDetails: AlertDef;
setUpdatedName: (name: string) => void;
intermediateName: string;
}): {
handleAlertUpdate: () => void;
isLoading: boolean;
} => {
const { notifications } = useNotifications();
const handleError = useAxiosError();
const { mutate: updateAlertRule, isLoading } = useMutation(
[REACT_QUERY_KEY.UPDATE_ALERT_RULE, alertDetails.id],
save,
{
onMutate: () => setUpdatedName(intermediateName),
onSuccess: () =>
notifications.success({ message: 'Alert renamed successfully' }),
onError: (error) => {
setUpdatedName(alertDetails.alert);
handleError(error);
},
},
);
const handleAlertUpdate = (): void => {
updateAlertRule({
data: { ...alertDetails, alert: intermediateName },
id: alertDetails.id,
});
};
return { handleAlertUpdate, isLoading };
};
export const useAlertRuleDelete = ({
ruleId,

View File

@@ -18,7 +18,6 @@ import {
} from '../MessagingQueuesUtils';
import DropRateView from '../MQDetails/DropRateView/DropRateView';
import MessagingQueueOverview from '../MQDetails/MessagingQueueOverview';
import MetricPage from '../MQDetails/MetricPage/MetricPage';
import MessagingQueuesDetails from '../MQDetails/MQDetails';
import MessagingQueuesConfigOptions from '../MQGraph/MQConfigOptions';
import MessagingQueuesGraph from '../MQGraph/MQGraph';
@@ -61,10 +60,6 @@ function MQDetailPage(): JSX.Element {
});
};
const showMessagingQueueDetails =
selectedView !== MessagingQueuesViewType.dropRate.value &&
selectedView !== MessagingQueuesViewType.metricPage.value;
return (
<div className="messaging-queue-container">
<div className="messaging-breadcrumb">
@@ -87,7 +82,7 @@ function MQDetailPage(): JSX.Element {
setSelectedView(value);
updateUrlQuery({ [QueryParams.mqServiceView]: value });
}}
value={selectedView}
value={mqServiceView}
options={[
{
label: MessagingQueuesViewType.consumerLag.label,
@@ -105,10 +100,6 @@ function MQDetailPage(): JSX.Element {
label: MessagingQueuesViewType.dropRate.label,
value: MessagingQueuesViewType.dropRate.value,
},
{
label: MessagingQueuesViewType.metricPage.label,
value: MessagingQueuesViewType.metricPage.value,
},
]}
/>
</div>
@@ -121,8 +112,6 @@ function MQDetailPage(): JSX.Element {
</div>
) : selectedView === MessagingQueuesViewType.dropRate.value ? (
<DropRateView />
) : selectedView === MessagingQueuesViewType.metricPage.value ? (
<MetricPage />
) : (
<MessagingQueueOverview
selectedView={selectedView}
@@ -130,7 +119,7 @@ function MQDetailPage(): JSX.Element {
setOption={setproducerLatencyOption}
/>
)}
{showMessagingQueueDetails && (
{selectedView !== MessagingQueuesViewType.dropRate.value && (
<div className="messaging-queue-details">
<MessagingQueuesDetails
selectedView={selectedView}

View File

@@ -28,16 +28,3 @@
width: 200px;
margin: 6px;
}
.lightMode {
.evaluation-time-selector {
.eval-title {
color: var(--bg-ink-400);
}
.ant-selector {
background-color: var(--bg-vanilla-200);
border: 1px solid var(--bg-ink-400);
}
}
}

View File

@@ -230,7 +230,9 @@ function DropRateView(): JSX.Element {
return (
<div className={cx('mq-overview-container', 'droprate-view')}>
<div className="mq-overview-title">
{MessagingQueuesViewType.dropRate.label}
<div className="drop-rat-title">
{MessagingQueuesViewType.dropRate.label}
</div>
<EvaluationTimeSelector setInterval={setInterval} />
</div>
<Table

View File

@@ -22,13 +22,15 @@
align-items: center;
width: 100%;
color: var(--bg-vanilla-200);
.drop-rat-title {
color: var(--bg-vanilla-200);
font-family: Inter;
font-size: 18px;
font-style: normal;
font-weight: 500;
line-height: 28px;
font-family: Inter;
font-size: 18px;
font-style: normal;
font-weight: 500;
line-height: 28px;
}
}
.mq-details-options {
@@ -114,67 +116,3 @@
}
}
}
.lightMode {
.mq-overview-container {
background: var(--bg-vanilla-200);
border: 1px solid var(--bg-vanilla-300);
.mq-overview-title {
color: var(--bg-ink-400);
}
.mq-details-options {
.ant-radio-button-wrapper {
border-color: var(--bg-vanilla-300);
color: var(--bg-slate-200);
}
.ant-radio-button-wrapper-checked {
color: var(--bg-slate-200);
background: var(--bg-vanilla-300);
}
.ant-radio-button-wrapper-disabled {
background: var(--bg-vanilla-100);
color: var(--bg-vanilla-400);
}
}
}
.droprate-view {
.mq-table {
.ant-table-content {
border: 1px solid var(--bg-vanilla-300);
}
.ant-table-tbody {
.ant-table-cell {
background-color: var(--bg-vanilla-100);
}
}
.ant-table-thead {
.ant-table-cell {
background-color: var(--bg-vanilla-100);
border-bottom: 1px solid var(--bg-vanilla-300);
}
}
}
.no-data-style {
border: 1px solid var(--bg-vanilla-300);
}
}
.trace-id-list {
.traceid-style {
.traceid-text {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
}
.remaing-count {
color: var(--bg-ink-400);
}
}
}
}

View File

@@ -18,6 +18,7 @@ import {
ProducerLatencyOptions,
SelectedTimelineQuery,
} from '../MessagingQueuesUtils';
import { ComingSoon } from '../MQCommon/MQCommon';
import MessagingQueuesTable from './MQTables/MQTables';
const MQServiceDetailTypePerView = (
@@ -27,6 +28,7 @@ const MQServiceDetailTypePerView = (
MessagingQueueServiceDetailType.ConsumerDetails,
MessagingQueueServiceDetailType.ProducerDetails,
MessagingQueueServiceDetailType.NetworkLatency,
MessagingQueueServiceDetailType.PartitionHostMetrics,
],
[MessagingQueuesViewType.partitionLatency.value]: [
MessagingQueueServiceDetailType.ConsumerDetails,
@@ -60,8 +62,22 @@ function MessagingQueuesOptions({
const detailTypes =
MQServiceDetailTypePerView(producerLatencyOption)[selectedView] || [];
return detailTypes.map((detailType) => (
<Radio.Button key={detailType} value={detailType}>
<Radio.Button
key={detailType}
value={detailType}
disabled={
detailType === MessagingQueueServiceDetailType.PartitionHostMetrics
}
className={
detailType === MessagingQueueServiceDetailType.PartitionHostMetrics
? 'disabled-option'
: ''
}
>
{ConsumerLagDetailTitle[detailType]}
{detailType === MessagingQueueServiceDetailType.PartitionHostMetrics && (
<ComingSoon />
)}
</Radio.Button>
));
};

View File

@@ -1,115 +0,0 @@
import { Typography } from 'antd';
import { CardContainer } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useTranslation } from 'react-i18next';
import { Widgets } from 'types/api/dashboard/getAll';
import MetricPageGridGraph from './MetricPageGraph';
import {
averageRequestLatencyWidgetData,
brokerCountWidgetData,
brokerNetworkThroughputWidgetData,
bytesConsumedWidgetData,
consumerFetchRateWidgetData,
consumerGroupMemberWidgetData,
consumerLagByGroupWidgetData,
consumerOffsetWidgetData,
ioWaitTimeWidgetData,
kafkaProducerByteRateWidgetData,
messagesConsumedWidgetData,
producerFetchRequestPurgatoryWidgetData,
requestResponseWidgetData,
requestTimesWidgetData,
} from './MetricPageUtil';
interface MetricSectionProps {
title: string;
description: string;
graphCount: Widgets[];
}
function MetricSection({
title,
description,
graphCount,
}: MetricSectionProps): JSX.Element {
const isDarkMode = useIsDarkMode();
return (
<div className="metric-column-graph">
<CardContainer className="row-card" isDarkMode={isDarkMode}>
<div className="row-panel">
<Typography.Text className="section-title">{title}</Typography.Text>
</div>
</CardContainer>
<Typography.Text className="graph-description">
{description}
</Typography.Text>
<div className="metric-page-grid">
{graphCount.map((widgetData) => (
<MetricPageGridGraph
key={`graph-${widgetData.id}`}
widgetData={widgetData}
/>
))}
</div>
</div>
);
}
function MetricColumnGraphs(): JSX.Element {
const { t } = useTranslation('messagingQueues');
const metricsData = [
{
title: t('metricGraphCategory.brokerMetrics.title'),
description: t('metricGraphCategory.brokerMetrics.description'),
graphCount: [
brokerCountWidgetData,
requestTimesWidgetData,
producerFetchRequestPurgatoryWidgetData,
brokerNetworkThroughputWidgetData,
],
id: 'broker-metrics',
},
{
title: t('metricGraphCategory.producerMetrics.title'),
description: t('metricGraphCategory.producerMetrics.description'),
graphCount: [
ioWaitTimeWidgetData,
requestResponseWidgetData,
averageRequestLatencyWidgetData,
kafkaProducerByteRateWidgetData,
bytesConsumedWidgetData,
],
id: 'producer-metrics',
},
{
title: t('metricGraphCategory.consumerMetrics.title'),
description: t('metricGraphCategory.consumerMetrics.description'),
graphCount: [
consumerOffsetWidgetData,
consumerGroupMemberWidgetData,
consumerLagByGroupWidgetData,
consumerFetchRateWidgetData,
messagesConsumedWidgetData,
],
id: 'consumer-metrics',
},
];
return (
<div className="metric-column-graph-container">
{metricsData.map((metric) => (
<MetricSection
key={metric.id}
title={metric.title}
description={metric.description}
graphCount={metric?.graphCount || []}
/>
))}
</div>
);
}
export default MetricColumnGraphs;

View File

@@ -1,128 +0,0 @@
.metric-page {
padding: 20px;
display: flex;
flex-direction: column;
gap: 32px;
.metric-page-container {
display: flex;
flex-direction: column;
.row-panel {
padding-left: 10px;
}
.metric-page-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
align-items: flex-start;
gap: 10px;
.metric-graph {
height: 320px;
padding: 10px;
width: 100%;
box-sizing: border-box;
}
}
@media (max-width: 768px) {
.metric-page-grid {
grid-template-columns: 1fr;
}
}
.graph-description {
padding: 16px 10px 16px 10px;
}
}
.row-panel {
border-radius: 4px;
background: rgba(18, 19, 23, 0.4);
padding: 8px;
display: flex;
gap: 6px;
align-items: center;
height: 48px !important;
.ant-typography {
font-size: 14px;
font-weight: 500;
}
.row-panel-section {
display: flex;
gap: 6px;
align-items: center;
.row-icon {
color: var(--bg-vanilla-400);
cursor: pointer;
}
.section-title {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px;
letter-spacing: -0.07px;
}
}
}
.metric-column-graph-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
.metric-column-graph {
display: flex;
flex-direction: column;
gap: 10px;
.row-panel {
justify-content: center;
}
.metric-page-grid {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
gap: 10px;
.metric-graph {
height: 320px;
padding: 10px;
width: 100%;
box-sizing: border-box;
}
}
}
}
@media (max-width: 768px) {
.metric-column-graph-container {
grid-template-columns: 1fr;
}
}
}
.lightMode {
.metric-page {
.row-panel {
.row-panel-section {
.row-icon {
color: var(--bg-ink-300);
}
.section-title {
color: var(--bg-ink-300);
}
}
}
}
}

View File

@@ -1,134 +0,0 @@
import './MetricPage.styles.scss';
import { Typography } from 'antd';
import cx from 'classnames';
import { CardContainer } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Widgets } from 'types/api/dashboard/getAll';
import MetricColumnGraphs from './MetricColumnGraphs';
import MetricPageGridGraph from './MetricPageGraph';
import {
cpuRecentUtilizationWidgetData,
currentOffsetPartitionWidgetData,
insyncReplicasWidgetData,
jvmGcCollectionsElapsedWidgetData,
jvmGCCountWidgetData,
jvmMemoryHeapWidgetData,
oldestOffsetWidgetData,
partitionCountPerTopicWidgetData,
} from './MetricPageUtil';
interface CollapsibleMetricSectionProps {
title: string;
description: string;
graphCount: Widgets[];
isCollapsed: boolean;
onToggle: () => void;
}
function CollapsibleMetricSection({
title,
description,
graphCount,
isCollapsed,
onToggle,
}: CollapsibleMetricSectionProps): JSX.Element {
const isDarkMode = useIsDarkMode();
return (
<div className="metric-page-container">
<CardContainer className="row-card" isDarkMode={isDarkMode}>
<div className={cx('row-panel')}>
<div className="row-panel-section">
<Typography.Text className="section-title">{title}</Typography.Text>
{isCollapsed ? (
<ChevronDown size={14} onClick={onToggle} className="row-icon" />
) : (
<ChevronUp size={14} onClick={onToggle} className="row-icon" />
)}
</div>
</div>
</CardContainer>
{!isCollapsed && (
<>
<Typography.Text className="graph-description">
{description}
</Typography.Text>
<div className="metric-page-grid">
{graphCount.map((widgetData) => (
<MetricPageGridGraph
key={`graph-${widgetData.id}`}
widgetData={widgetData}
/>
))}
</div>
</>
)}
</div>
);
}
function MetricPage(): JSX.Element {
const [collapsedSections, setCollapsedSections] = useState<{
[key: string]: boolean;
}>({
producerMetrics: false,
consumerMetrics: false,
});
const toggleCollapse = (key: string): void => {
setCollapsedSections((prev) => ({
...prev,
[key]: !prev[key],
}));
};
const { t } = useTranslation('messagingQueues');
const metricSections = [
{
key: 'bokerJVMMetrics',
title: t('metricGraphCategory.brokerJVMMetrics.title'),
description: t('metricGraphCategory.brokerJVMMetrics.description'),
graphCount: [
jvmGCCountWidgetData,
jvmGcCollectionsElapsedWidgetData,
cpuRecentUtilizationWidgetData,
jvmMemoryHeapWidgetData,
],
},
{
key: 'partitionMetrics',
title: t('metricGraphCategory.partitionMetrics.title'),
description: t('metricGraphCategory.partitionMetrics.description'),
graphCount: [
partitionCountPerTopicWidgetData,
currentOffsetPartitionWidgetData,
oldestOffsetWidgetData,
insyncReplicasWidgetData,
],
},
];
return (
<div className="metric-page">
<MetricColumnGraphs />
{metricSections.map(({ key, title, description, graphCount }) => (
<CollapsibleMetricSection
key={key}
title={title}
description={description}
graphCount={graphCount}
isCollapsed={collapsedSections[key]}
onToggle={(): void => toggleCollapse(key)}
/>
))}
</div>
);
}
export default MetricPage;

View File

@@ -1,59 +0,0 @@
import './MetricPage.styles.scss';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { ViewMenuAction } from 'container/GridCardLayout/config';
import GridCard from 'container/GridCardLayout/GridCard';
import { Card } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useUrlQuery from 'hooks/useUrlQuery';
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions';
import { Widgets } from 'types/api/dashboard/getAll';
function MetricPageGridGraph({
widgetData,
}: {
widgetData: Widgets;
}): JSX.Element {
const history = useHistory();
const { pathname } = useLocation();
const dispatch = useDispatch();
const urlQuery = useUrlQuery();
const isDarkMode = useIsDarkMode();
const onDragSelect = useCallback(
(start: number, end: number) => {
const startTimestamp = Math.trunc(start);
const endTimestamp = Math.trunc(end);
urlQuery.set(QueryParams.startTime, startTimestamp.toString());
urlQuery.set(QueryParams.endTime, endTimestamp.toString());
const generatedUrl = `${pathname}?${urlQuery.toString()}`;
history.push(generatedUrl);
if (startTimestamp !== endTimestamp) {
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
}
},
[dispatch, history, pathname, urlQuery],
);
return (
<Card
isDarkMode={isDarkMode}
$panelType={PANEL_TYPES.TIME_SERIES}
className="metric-graph"
>
<GridCard
widget={widgetData}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
/>
</Card>
);
}
export default MetricPageGridGraph;

View File

@@ -166,77 +166,3 @@
padding-right: 8px;
}
}
.lightMode {
.mq-health-check-modal {
.ant-modal-content {
border: 1px solid var(--bg-vanilla-400);
background: var(--bg-vanilla-200);
.ant-modal-header {
border-bottom: 1px solid var(--bg-vanilla-400);
background: var(--bg-vanilla-200);
.ant-modal-title {
color: var(--bg-ink-300);
}
}
.modal-content {
background: var(--bg-vanilla-100);
.attribute-select {
.ant-select-selector {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-200);
}
}
.tree-text {
color: var(--bg-ink-300);
}
.ant-tree {
.ant-tree-title {
.attribute-error-title {
color: var(--bg-amber-500);
.tree-text {
color: var(--bg-amber-500);
}
}
.attribute-success-title {
.success-attribute-icon {
color: var(--bg-ink-300);
}
}
}
}
}
.loader-container {
background: var(--bg-ink-300);
}
}
}
.config-btn {
background: var(--bg-vanilla-300);
&.missing-config-btn {
background: var(--bg-amber-100);
color: var(--bg-amber-500);
&:hover {
color: var(--bg-amber-600) !important;
}
}
.missing-config-btn {
.config-btn-content {
border-right: 1px solid var(--bg-amber-600);
}
}
}
}

View File

@@ -222,12 +222,6 @@
}
}
:nth-child(2),
:nth-child(4) {
border-left: none !important;
border-right: none !important;
}
&.summary-section {
.overview-info-card {
min-height: 144px;
@@ -337,10 +331,6 @@
.messaging-breadcrumb {
color: var(--bg-ink-400);
border-bottom: 1px solid var(--bg-vanilla-300);
.message-queue-text {
color: var(--bg-ink-400);
}
}
.messaging-header {
color: var(--bg-ink-400);

View File

@@ -156,7 +156,7 @@ function MessagingQueues(): JSX.Element {
</Button>
</div>
</div>
<div className="overview-info-card">
<div className="overview-info-card middle-card">
<div>
<p className="card-title">{t('summarySection.producer.title')}</p>
<p className="card-info-text">
@@ -174,7 +174,7 @@ function MessagingQueues(): JSX.Element {
</Button>
</div>
</div>
<div className="overview-info-card">
<div className="overview-info-card middle-card">
<div>
<p className="card-title">{t('summarySection.partition.title')}</p>
<p className="card-info-text">
@@ -210,24 +210,6 @@ function MessagingQueues(): JSX.Element {
</Button>
</div>
</div>
<div className="overview-info-card">
<div>
<p className="card-title">{t('summarySection.metricPage.title')}</p>
<p className="card-info-text">
{t('summarySection.metricPage.description')}
</p>
</div>
<div className="button-grp">
<Button
type="default"
onClick={(): void =>
redirectToDetailsPage(MessagingQueuesViewType.metricPage.value)
}
>
{t('summarySection.viewDetailsButton')}
</Button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -222,8 +222,7 @@ export enum MessagingQueuesViewTypeOptions {
ConsumerLag = 'consumerLag',
PartitionLatency = 'partitionLatency',
ProducerLatency = 'producerLatency',
DropRate = 'dropRate',
MetricPage = 'metricPage',
ConsumerLatency = 'consumerLatency',
}
export const MessagingQueuesViewType = {
@@ -241,11 +240,7 @@ export const MessagingQueuesViewType = {
},
dropRate: {
label: 'Drop Rate view',
value: MessagingQueuesViewTypeOptions.DropRate,
},
metricPage: {
label: 'Metric view',
value: MessagingQueuesViewTypeOptions.MetricPage,
value: 'dropRate',
},
};

View File

@@ -19,7 +19,7 @@ export const defaultSeasonality = 'hourly';
export interface AlertDef {
id?: number;
alertType?: string;
alert: string;
alert?: string;
ruleType?: string;
frequency?: string;
condition: RuleCondition;

View File

@@ -860,6 +860,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/parser@^7.23.6":
version "7.23.6"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b"
integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==
"@babel/parser@^7.24.0", "@babel/parser@^7.24.1":
version "7.24.1"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a"
@@ -3094,6 +3099,48 @@
"@types/mdx" "^2.0.0"
"@types/react" ">=16"
"@microsoft/api-extractor-model@7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.28.3.tgz#f6a213e41a2274d5195366b646954daee39e8493"
integrity sha512-wT/kB2oDbdZXITyDh2SQLzaWwTOFbV326fP0pUwNW00WeliARs0qjmXBWmGWardEzp2U3/axkO3Lboqun6vrig==
dependencies:
"@microsoft/tsdoc" "0.14.2"
"@microsoft/tsdoc-config" "~0.16.1"
"@rushstack/node-core-library" "3.62.0"
"@microsoft/api-extractor@7.39.0":
version "7.39.0"
resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.39.0.tgz#41c25f7f522e8b9376debda07364ff234e602eff"
integrity sha512-PuXxzadgnvp+wdeZFPonssRAj/EW4Gm4s75TXzPk09h3wJ8RS3x7typf95B4vwZRrPTQBGopdUl+/vHvlPdAcg==
dependencies:
"@microsoft/api-extractor-model" "7.28.3"
"@microsoft/tsdoc" "0.14.2"
"@microsoft/tsdoc-config" "~0.16.1"
"@rushstack/node-core-library" "3.62.0"
"@rushstack/rig-package" "0.5.1"
"@rushstack/ts-command-line" "4.17.1"
colors "~1.2.1"
lodash "~4.17.15"
resolve "~1.22.1"
semver "~7.5.4"
source-map "~0.6.1"
typescript "5.3.3"
"@microsoft/tsdoc-config@~0.16.1":
version "0.16.2"
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf"
integrity sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==
dependencies:
"@microsoft/tsdoc" "0.14.2"
ajv "~6.12.6"
jju "~1.4.0"
resolve "~1.19.0"
"@microsoft/tsdoc@0.14.2":
version "0.14.2"
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb"
integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==
"@monaco-editor/loader@^1.3.3":
version "1.3.3"
resolved "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.3.tgz"
@@ -3499,6 +3546,46 @@
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.20.0.tgz#03554155b45d8b529adf635b2f6ad1165d70d8b4"
integrity sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==
"@rollup/pluginutils@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0"
integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==
dependencies:
"@types/estree" "^1.0.0"
estree-walker "^2.0.2"
picomatch "^2.3.1"
"@rushstack/node-core-library@3.62.0":
version "3.62.0"
resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.62.0.tgz#a30a44a740b522944165f0faa6644134eb95be1d"
integrity sha512-88aJn2h8UpSvdwuDXBv1/v1heM6GnBf3RjEy6ZPP7UnzHNCqOHA2Ut+ScYUbXcqIdfew9JlTAe3g+cnX9xQ/Aw==
dependencies:
colors "~1.2.1"
fs-extra "~7.0.1"
import-lazy "~4.0.0"
jju "~1.4.0"
resolve "~1.22.1"
semver "~7.5.4"
z-schema "~5.0.2"
"@rushstack/rig-package@0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.5.1.tgz#6c9c283cc96b5bb1eae9875946d974ac5429bb21"
integrity sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==
dependencies:
resolve "~1.22.1"
strip-json-comments "~3.1.1"
"@rushstack/ts-command-line@4.17.1":
version "4.17.1"
resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz#c78db928ce5b93f2e98fd9e14c24f3f3876e57f1"
integrity sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==
dependencies:
"@types/argparse" "1.0.38"
argparse "~1.0.9"
colors "~1.2.1"
string-argv "~0.3.1"
"@sentry-internal/feedback@7.102.1":
version "7.102.1"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.102.1.tgz#747f88c2881c76fddd16bce57cc4bc17b4c2af93"
@@ -3663,10 +3750,13 @@
unplugin "1.0.1"
uuid "^9.0.0"
"@signozhq/design-tokens@1.1.4":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@signozhq/design-tokens/-/design-tokens-1.1.4.tgz#5d5de5bd9d19b6a3631383db015cc4b70c3f7661"
integrity sha512-ICZz5szxTq8NcKAsk6LP+nSybPyEcyy8eu2zfxlPQCnJ1YjJP1PglaKLlF0N6+D60gAd3yC5he06BqR8/HxjNg==
"@signozhq/design-tokens@0.0.8":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@signozhq/design-tokens/-/design-tokens-0.0.8.tgz#368dc92cfe01d0cd893df140445c5d9dfd944a88"
integrity sha512-YUxQw6w7iyUMTBxj82nFZQNRsg7Boej3YM6K5bYfDMQg0MqvWQCWsP7EkyLHu/TiyOZwZWb++vzXG6m+YJX9bw==
dependencies:
style-dictionary "3.8.0"
vite-plugin-dts "^3.6.4"
"@sinclair/typebox@^0.25.16":
version "0.25.24"
@@ -3786,6 +3876,11 @@
dependencies:
"@types/estree" "*"
"@types/argparse@1.0.38":
version "1.0.38"
resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9"
integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==
"@types/aria-query@^5.0.1":
version "5.0.1"
resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz"
@@ -4764,7 +4859,68 @@
d3-time-format "4.1.0"
internmap "2.0.3"
"@webassemblyjs/ast@1.12.1":
"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-1.11.1.tgz#ecdf12ea8dc35fb8549e517991abcbf449a5ad4f"
integrity sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==
dependencies:
"@volar/source-map" "1.11.1"
"@volar/source-map@1.11.1", "@volar/source-map@~1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-1.11.1.tgz#535b0328d9e2b7a91dff846cab4058e191f4452f"
integrity sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==
dependencies:
muggle-string "^0.3.1"
"@volar/typescript@~1.11.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-1.11.1.tgz#ba86c6f326d88e249c7f5cfe4b765be3946fd627"
integrity sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==
dependencies:
"@volar/language-core" "1.11.1"
path-browserify "^1.0.1"
"@vue/compiler-core@3.4.4":
version "3.4.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.4.tgz#ba1ca008e95f118449cab79bdab3f7506bab2892"
integrity sha512-U5AdCN+6skzh2bSJrkMj2KZsVkUpgK8/XlxjSRYQZhNPcvt9/kmgIMpFEiTyK+Dz5E1J+8o8//BEIX+bakgVSw==
dependencies:
"@babel/parser" "^7.23.6"
"@vue/shared" "3.4.4"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.0.2"
"@vue/compiler-dom@^3.3.0":
version "3.4.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.4.tgz#a11bba8af691b58700c479ce893b02bf71bb089a"
integrity sha512-iSwkdDULCN+Vr8z6uwdlL044GJ/nUmECxP9vu7MzEs4Qma0FwDLYvnvRcyO0ZITuu3Os4FptGUDnhi1kOLSaGw==
dependencies:
"@vue/compiler-core" "3.4.4"
"@vue/shared" "3.4.4"
"@vue/language-core@1.8.27", "@vue/language-core@^1.8.26":
version "1.8.27"
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.27.tgz#2ca6892cb524e024a44e554e4c55d7a23e72263f"
integrity sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==
dependencies:
"@volar/language-core" "~1.11.1"
"@volar/source-map" "~1.11.1"
"@vue/compiler-dom" "^3.3.0"
"@vue/shared" "^3.3.0"
computeds "^0.0.1"
minimatch "^9.0.3"
muggle-string "^0.3.1"
path-browserify "^1.0.1"
vue-template-compiler "^2.7.14"
"@vue/shared@3.4.4", "@vue/shared@^3.3.0":
version "3.4.4"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.4.tgz#26e4e62a5fbfb39c25e9e54d21eeb852f1c83a7a"
integrity sha512-abSgiVRhfjfl3JALR/cSuBl74hGJ3SePgf1mKzodf1eMWLwHZbfEGxT2cNJSsNiw44jEgrO7bNkhchaWA7RwNw==
"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
version "1.12.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb"
integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==
@@ -4772,34 +4928,16 @@
"@webassemblyjs/helper-numbers" "1.11.6"
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
"@webassemblyjs/ast@^1.12.1":
version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6"
integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
dependencies:
"@webassemblyjs/helper-numbers" "1.13.2"
"@webassemblyjs/helper-wasm-bytecode" "1.13.2"
"@webassemblyjs/floating-point-hex-parser@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431"
integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==
"@webassemblyjs/floating-point-hex-parser@1.13.2":
version "1.13.2"
resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb"
integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==
"@webassemblyjs/helper-api-error@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768"
integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==
"@webassemblyjs/helper-api-error@1.13.2":
version "1.13.2"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7"
integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==
"@webassemblyjs/helper-buffer@1.12.1":
version "1.12.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6"
@@ -4814,25 +4952,11 @@
"@webassemblyjs/helper-api-error" "1.11.6"
"@xtuc/long" "4.2.2"
"@webassemblyjs/helper-numbers@1.13.2":
version "1.13.2"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d"
integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==
dependencies:
"@webassemblyjs/floating-point-hex-parser" "1.13.2"
"@webassemblyjs/helper-api-error" "1.13.2"
"@xtuc/long" "4.2.2"
"@webassemblyjs/helper-wasm-bytecode@1.11.6":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9"
integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==
"@webassemblyjs/helper-wasm-bytecode@1.13.2":
version "1.13.2"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b"
integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==
"@webassemblyjs/helper-wasm-section@1.12.1":
version "1.12.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf"
@@ -5099,7 +5223,7 @@ ajv-keywords@^5.1.0:
dependencies:
fast-deep-equal "^3.1.3"
ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6:
ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6, ajv@~6.12.6:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -5258,7 +5382,7 @@ arg@^4.1.0:
resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7:
argparse@^1.0.7, argparse@~1.0.9:
version "1.0.10"
resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz"
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
@@ -6174,6 +6298,15 @@ canvas-color-tracker@1:
dependencies:
tinycolor2 "^1.6.0"
capital-case@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case-first "^2.0.2"
cardboard-vr-display@^1.0.19:
version "1.0.19"
resolved "https://registry.npmjs.org/cardboard-vr-display/-/cardboard-vr-display-1.0.19.tgz"
@@ -6220,6 +6353,24 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
change-case@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12"
integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==
dependencies:
camel-case "^4.1.2"
capital-case "^1.0.4"
constant-case "^3.0.4"
dot-case "^3.0.4"
header-case "^2.0.4"
no-case "^3.0.4"
param-case "^3.0.4"
pascal-case "^3.1.2"
path-case "^3.0.4"
sentence-case "^3.0.4"
snake-case "^3.0.4"
tslib "^2.0.3"
char-regex@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz"
@@ -6471,6 +6622,11 @@ colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16:
resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
colors@~1.2.1:
version "1.2.5"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc"
integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -6493,6 +6649,11 @@ commander@2, commander@^2.20.0, commander@^2.20.3:
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commander@^7.0.0, commander@^7.2.0:
version "7.2.0"
resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz"
@@ -6562,6 +6723,11 @@ compute-scroll-into-view@^3.0.2:
resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz"
integrity sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==
computeds@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/computeds/-/computeds-0.0.1.tgz#215b08a4ba3e08a11ff6eee5d6d8d7166a97ce2e"
integrity sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
@@ -6577,6 +6743,15 @@ connect-history-api-fallback@^2.0.0:
resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz"
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
constant-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case "^2.0.2"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
@@ -7219,6 +7394,11 @@ dayjs@^1.10.7, dayjs@^1.11.1:
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz"
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
debounce@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
@@ -7670,7 +7850,7 @@ entities@^2.0.0, entities@^2.2.0:
resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@^4.2.0, entities@^4.4.0:
entities@^4.2.0, entities@^4.4.0, entities@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
@@ -8216,6 +8396,11 @@ estree-util-visit@^1.0.0:
"@types/estree-jsx" "^1.0.0"
"@types/unist" "^2.0.0"
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
estree-walker@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
@@ -8634,6 +8819,15 @@ fs-extra@^10.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@~7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
dependencies:
graceful-fs "^4.1.2"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-monkey@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz"
@@ -8788,7 +8982,7 @@ glob-to-regexp@^0.4.1:
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
version "7.2.3"
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -9186,6 +9380,14 @@ he@^1.2.0:
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
header-case@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063"
integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==
dependencies:
capital-case "^1.0.4"
tslib "^2.0.3"
headers-polyfill@3.2.5:
version "3.2.5"
resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.2.5.tgz#6e67d392c9d113d37448fe45014e0afdd168faed"
@@ -9484,6 +9686,11 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
parent-module "^1.0.0"
resolve-from "^4.0.0"
import-lazy@~4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz"
@@ -9704,6 +9911,13 @@ is-ci@^3.0.1:
dependencies:
ci-info "^3.2.0"
is-core-module@^2.1.0:
version "2.13.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
dependencies:
hasown "^2.0.0"
is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0:
version "2.12.0"
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz"
@@ -10553,6 +10767,11 @@ jest@^27.5.1:
import-local "^3.0.2"
jest-cli "^27.5.1"
jju@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
js-base64@^3.7.2:
version "3.7.5"
resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz"
@@ -10678,6 +10897,18 @@ json5@^1.0.2:
dependencies:
minimist "^1.2.0"
jsonc-parser@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz"
@@ -10737,6 +10968,11 @@ klona@^2.0.4:
resolved "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz"
integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==
kolorist@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c"
integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
language-subtag-registry@~0.3.2:
version "0.3.22"
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
@@ -10963,7 +11199,12 @@ lodash.debounce@^4.0.8:
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.isequal@^4.0.0:
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
lodash.isequal@^4.0.0, lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
@@ -10993,7 +11234,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0, lodash@~4.17.15:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -11923,6 +12164,13 @@ minimatch@^8.0.2:
dependencies:
brace-expansion "^2.0.1"
minimatch@^9.0.3:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
dependencies:
brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz"
@@ -12016,6 +12264,11 @@ msw@1.3.2:
type-fest "^2.19.0"
yargs "^17.3.1"
muggle-string@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.3.1.tgz#e524312eb1728c63dd0b2ac49e3282e6ed85963a"
integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==
multicast-dns@^7.2.5:
version "7.2.5"
resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz"
@@ -12691,6 +12944,19 @@ pascal-case@^3.1.2:
no-case "^3.0.4"
tslib "^2.0.3"
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"
integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
@@ -12716,7 +12982,7 @@ path-key@^3.0.0, path-key@^3.1.0:
resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.7:
path-parse@^1.0.6, path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
@@ -14588,6 +14854,23 @@ resolve@^2.0.0-next.4:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@~1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c"
integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
dependencies:
is-core-module "^2.1.0"
path-parse "^1.0.6"
resolve@~1.22.1:
version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
dependencies:
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
restore-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz"
@@ -14782,7 +15065,7 @@ selfsigned@^2.1.1:
dependencies:
node-forge "^1"
"semver@2 || 3 || 4 || 5", semver@7.3.7, semver@7.5.4, semver@7.x, semver@^5.5.0, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7:
"semver@2 || 3 || 4 || 5", semver@7.3.7, semver@7.5.4, semver@7.x, semver@^5.5.0, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4, semver@~7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
@@ -14808,6 +15091,15 @@ send@0.19.0:
range-parser "~1.2.1"
statuses "2.0.1"
sentence-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case-first "^2.0.2"
serialize-javascript@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz"
@@ -15013,6 +15305,14 @@ slice-ansi@^5.0.0:
ansi-styles "^6.0.0"
is-fullwidth-code-point "^4.0.0"
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
sockjs@^0.3.24:
version "0.3.24"
resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz"
@@ -15241,6 +15541,11 @@ string-argv@^0.3.1:
resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz"
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
string-argv@~0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
string-convert@^0.2.0:
version "0.2.1"
resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz"
@@ -15385,11 +15690,26 @@ strip-indent@^3.0.0:
dependencies:
min-indent "^1.0.0"
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1:
version "3.1.1"
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
style-dictionary@3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/style-dictionary/-/style-dictionary-3.8.0.tgz#7cb8d64360c53431f768d44def665f61e971a73e"
integrity sha512-wHlB/f5eO3mDcYv6WtOz6gvQC477jBKrwuIXe+PtHskTCBsJdAOvL8hCquczJxDui2TnwpeNE+2msK91JJomZg==
dependencies:
chalk "^4.0.0"
change-case "^4.1.2"
commander "^8.3.0"
fs-extra "^10.0.0"
glob "^7.2.0"
json5 "^2.2.2"
jsonc-parser "^3.0.0"
lodash "^4.17.15"
tinycolor2 "^1.4.1"
style-loader@1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz"
@@ -15716,7 +16036,7 @@ tiny-warning@^1.0.0:
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tinycolor2@1, tinycolor2@1.6.0, tinycolor2@^1.6.0:
tinycolor2@1, tinycolor2@1.6.0, tinycolor2@^1.4.1, tinycolor2@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
@@ -16039,6 +16359,11 @@ typescript-plugin-css-modules@5.0.1:
stylus "^0.59.0"
tsconfig-paths "^4.1.2"
typescript@5.3.3:
version "5.3.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
typescript@^4.0.5, typescript@^4.4.3:
version "4.9.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz"
@@ -16195,6 +16520,11 @@ unist-util-visit@^5.0.0:
unist-util-is "^6.0.0"
unist-util-visit-parents "^6.0.0"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz"
@@ -16241,6 +16571,20 @@ uplot@1.6.31:
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.31.tgz#092a4b586590e9794b679e1df885a15584b03698"
integrity sha512-sQZqSwVCbJGnFB4IQjQYopzj5CoTZJ4Br1fG/xdONimqgHmsacvCjNesdGDypNKFbrhLGIeshYhy89FxPF+H+w==
upper-case-first@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==
dependencies:
tslib "^2.0.3"
upper-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a"
integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==
dependencies:
tslib "^2.0.3"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
@@ -16349,6 +16693,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
validator@^13.7.0:
version "13.11.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b"
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
@@ -16410,11 +16759,40 @@ vfile@^6.0.0:
unist-util-stringify-position "^4.0.0"
vfile-message "^4.0.0"
vite-plugin-dts@^3.6.4:
version "3.7.0"
resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-3.7.0.tgz#654ee7c38c0cdd4589b9bc198a264f34172bd870"
integrity sha512-np1uPaYzu98AtPReB8zkMnbjwcNHOABsLhqVOf81b3ol9b5M2wPcAVs8oqPnOpr6Us+7yDXVauwkxsk5+ldmRA==
dependencies:
"@microsoft/api-extractor" "7.39.0"
"@rollup/pluginutils" "^5.1.0"
"@vue/language-core" "^1.8.26"
debug "^4.3.4"
kolorist "^1.8.0"
vue-tsc "^1.8.26"
void-elements@3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz"
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
vue-template-compiler@^2.7.14:
version "2.7.16"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b"
integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==
dependencies:
de-indent "^1.0.2"
he "^1.2.0"
vue-tsc@^1.8.26:
version "1.8.27"
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.27.tgz#feb2bb1eef9be28017bb9e95e2bbd1ebdd48481c"
integrity sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==
dependencies:
"@volar/typescript" "~1.11.1"
"@vue/language-core" "1.8.27"
semver "^7.5.4"
w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz"
@@ -17014,6 +17392,17 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
z-schema@~5.0.2:
version "5.0.6"
resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5"
integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg==
dependencies:
lodash.get "^4.4.2"
lodash.isequal "^4.5.0"
validator "^13.7.0"
optionalDependencies:
commander "^10.0.0"
zwitch@^2.0.0, zwitch@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"

2
go.mod
View File

@@ -47,6 +47,7 @@ require (
github.com/sethvargo/go-password v0.2.0
github.com/smartystreets/goconvey v1.8.1
github.com/soheilhy/cmux v0.1.5
github.com/spf13/cobra v1.8.1
github.com/srikanthccv/ClickHouse-go-mock v0.9.0
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/collector/component v0.111.0
@@ -169,7 +170,6 @@ require (
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smarty/assertions v1.15.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect

View File

@@ -2,15 +2,43 @@ package config
import (
"context"
"os"
signozconfmap "go.signoz.io/signoz/pkg/confmap"
"go.signoz.io/signoz/pkg/instrumentation"
"go.signoz.io/signoz/pkg/query-service/app/clickhouseReader"
"go.signoz.io/signoz/pkg/query-service/app/opamp"
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/cache"
"go.signoz.io/signoz/pkg/query-service/dao"
"go.signoz.io/signoz/pkg/version"
"go.signoz.io/signoz/pkg/web"
)
// This map contains the default values of all config structs
var (
defaultMap map[string]signozconfmap.Config = map[string]signozconfmap.Config{
"version": &version.Config{},
"instrumentation": &instrumentation.Config{},
"web": &web.Config{},
"cache": &cache.Config{},
"auth": &auth.Config{},
"database": &dao.Config{},
"storage": &clickhouseReader.Config{},
"opamp": &opamp.Config{},
}
)
// Config defines the entire configuration of signoz.
type Config struct {
Instrumentation instrumentation.Config `mapstructure:"instrumentation"`
Web web.Config `mapstructure:"web"`
Version version.Config `mapstructure:"version"`
Instrumentation instrumentation.Config `mapstructure:"instrumentation"`
Web web.Config `mapstructure:"web"`
Cache cache.Config `mapstructure:"cache"`
Auth auth.Config `mapstructure:"auth"`
Database dao.Config `mapstructure:"database"`
Storage clickhouseReader.Config `mapstructure:"storage"`
Opamp opamp.Config `mapstructure:"opamp"`
}
func New(ctx context.Context, settings ProviderSettings) (*Config, error) {
@@ -22,14 +50,13 @@ func New(ctx context.Context, settings ProviderSettings) (*Config, error) {
return provider.Get(ctx)
}
func byName(name string) (any, bool) {
switch name {
case "instrumentation":
return &instrumentation.Config{}, true
case "web":
return &web.Config{}, true
default:
return nil, false
// A backwards compatibility function to ensure signoz does not break for existing
// users. This will modify the input config in place
func EnsureBackwardsCompatibility(ctx context.Context, instrumentation *instrumentation.Instrumentation, cfg *Config) {
jwtSecret, ok := os.LookupEnv("SIGNOZ_JWT_SECRET")
if ok {
instrumentation.Logger.Warn("SIGNOZ_JWT_SECRET has been deprecated and will be removed in a future release")
cfg.Auth.Jwt.Secret = jwtSecret
}
}

View File

@@ -10,6 +10,7 @@ import (
contribsdkconfig "go.opentelemetry.io/contrib/config"
"go.signoz.io/signoz/pkg/confmap/provider/signozenvprovider"
"go.signoz.io/signoz/pkg/instrumentation"
"go.signoz.io/signoz/pkg/version"
"go.signoz.io/signoz/pkg/web"
)
@@ -32,6 +33,11 @@ func TestNewWithSignozEnvProvider(t *testing.T) {
i := 10
expected := &Config{
Version: version.Config{
Banner: version.Banner{
Enabled: true,
},
},
Instrumentation: instrumentation.Config{
Logs: instrumentation.LogsConfig{
Enabled: true,
@@ -57,5 +63,7 @@ func TestNewWithSignozEnvProvider(t *testing.T) {
},
}
assert.Equal(t, expected, config)
assert.Equal(t, expected.Instrumentation, config.Instrumentation)
assert.Equal(t, expected.Web, config.Web)
assert.Equal(t, expected.Version, config.Version)
}

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"go.opentelemetry.io/collector/confmap"
signozconfmap "go.signoz.io/signoz/pkg/confmap"
)
// unmarshal converts a confmap.Conf into a Config struct.
@@ -17,24 +16,17 @@ func unmarshal(conf *confmap.Conf) (*Config, error) {
return nil, err
}
// To help the defaults kick in, we need to put keys into the raw map if the key is not present
parsed := make(map[string]any)
for k := range raw {
e, ok := byName(k)
if !ok {
return nil, fmt.Errorf("cannot find config with name %q", k)
}
i, ok := e.(signozconfmap.Config)
if !ok {
return nil, fmt.Errorf("config %q does not implement \"signozconfmap.Config\"", k)
}
for k, v := range defaultMap {
sub, err := conf.Sub(k)
if err != nil {
return nil, fmt.Errorf("cannot read config for %q: %w", k, err)
}
d := i.NewWithDefaults()
d := v.NewWithDefaults()
if err := sub.Unmarshal(&d); err != nil {
return nil, fmt.Errorf("cannot merge config for %q: %w", k, err)
}

View File

@@ -29,6 +29,6 @@ func TestUnmarshal(t *testing.T) {
cfg, err := unmarshal(input)
require.NoError(t, err)
assert.Equal(t, expected, cfg)
assert.Equal(t, expected.Instrumentation, cfg.Instrumentation)
}

View File

@@ -0,0 +1,41 @@
package clickhouseReader
import (
"time"
"go.signoz.io/signoz/pkg/confmap"
)
type Config struct {
Provider string `mapstructure:"provider"`
DSN string `mapstructure:"dsn"`
Cluster string `mapstructure:"cluster"`
PrometheusConfigPath string `mapstructure:"prometheus_config_path"`
MaxIdleConnections int `mapstructure:"max_idle_connections"`
MaxOpenConnections int `mapstructure:"max_open_connections"`
DialTimeout time.Duration `mapstructure:"dial_timeout"`
OptimizeReadInOrderRegex string `mapstructure:"optimize_read_in_order_regex"`
MaxExecutionTimeLeaf string `mapstructure:"max_execution_time_leaf"`
TimeoutBeforeCheckingExecutionSpeed string `mapstructure:"timeout_before_checking_execution_speed"`
MaxBytesToRead string `mapstructure:"max_bytes_to_read"`
}
func (c *Config) NewWithDefaults() confmap.Config {
return &Config{
Provider: "clickhouse",
DSN: "tcp://localhost:9000",
Cluster: "cluster",
PrometheusConfigPath: "/etc/signoz/config/prometheus.yml",
MaxIdleConnections: 50,
MaxOpenConnections: 100,
DialTimeout: 5 * time.Second,
OptimizeReadInOrderRegex: "",
MaxExecutionTimeLeaf: "",
TimeoutBeforeCheckingExecutionSpeed: "",
MaxBytesToRead: "",
}
}
func (c *Config) Validate() error {
return nil
}

View File

@@ -162,6 +162,7 @@ func NewReader(
useLogsNewSchema bool,
) *ClickHouseReader {
// todo(remove): read from config
datasource := os.Getenv("ClickHouseUrl")
options := NewOptions(datasource, maxIdleConns, maxOpenConns, dialTimeout, primaryNamespace, archiveNamespace)
db, err := initialize(options)

View File

@@ -46,6 +46,7 @@ import (
"go.signoz.io/signoz/pkg/query-service/contextlinks"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
"go.signoz.io/signoz/pkg/query-service/postprocess"
"go.signoz.io/signoz/pkg/version"
"go.uber.org/zap"
@@ -53,12 +54,10 @@ import (
"go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline"
"go.signoz.io/signoz/pkg/query-service/dao"
am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
signozio "go.signoz.io/signoz/pkg/query-service/integrations/signozio"
"go.signoz.io/signoz/pkg/query-service/interfaces"
"go.signoz.io/signoz/pkg/query-service/model"
"go.signoz.io/signoz/pkg/query-service/rules"
"go.signoz.io/signoz/pkg/query-service/telemetry"
"go.signoz.io/signoz/pkg/query-service/version"
)
type status string
@@ -111,7 +110,6 @@ type APIHandler struct {
Upgrader *websocket.Upgrader
UseLogsNewSchema bool
UseLicensesV3 bool
hostsRepo *inframetrics.HostsRepo
processesRepo *inframetrics.ProcessesRepo
@@ -157,9 +155,6 @@ type APIHandlerOpts struct {
// Use Logs New schema
UseLogsNewSchema bool
// Use Licenses V3 structure
UseLicensesV3 bool
}
// NewAPIHandler returns an APIHandler
@@ -215,7 +210,6 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
querier: querier,
querierV2: querierv2,
UseLogsNewSchema: opts.UseLogsNewSchema,
UseLicensesV3: opts.UseLicensesV3,
hostsRepo: hostsRepo,
processesRepo: processesRepo,
podsRepo: podsRepo,
@@ -488,7 +482,6 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
router.HandleFunc("/api/v1/version", am.OpenAccess(aH.getVersion)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/featureFlags", am.OpenAccess(aH.getFeatureFlags)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/configs", am.OpenAccess(aH.getConfigs)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/health", am.OpenAccess(aH.getHealth)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/getSpanFilters", am.ViewAccess(aH.getSpanFilters)).Methods(http.MethodPost)
@@ -1937,9 +1930,8 @@ func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) {
}
func (aH *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) {
version := version.GetVersion()
versionResponse := model.GetVersionResponse{
Version: version,
Version: version.Info.Version,
EE: "Y",
SetupCompleted: aH.SetupCompleted,
}
@@ -1973,16 +1965,6 @@ func (aH *APIHandler) CheckFeature(f string) bool {
return err == nil
}
func (aH *APIHandler) getConfigs(w http.ResponseWriter, r *http.Request) {
configs, err := signozio.FetchDynamicConfigs()
if err != nil {
aH.HandleError(w, err, http.StatusInternalServerError)
return
}
aH.Respond(w, configs)
}
// getHealth is used to check the health of the service.
// 'live' query param can be used to check liveliness of
// the service by checking the database connection.
@@ -3222,16 +3204,16 @@ func (aH *APIHandler) getProducerThroughputOverview(
}
for _, res := range result {
for _, list := range res.List {
serviceName, serviceNameOk := list.Data["service_name"].(*string)
topicName, topicNameOk := list.Data["topic"].(*string)
params := []string{*serviceName, *topicName}
for _, series := range res.Series {
serviceName, serviceNameOk := series.Labels["service_name"]
topicName, topicNameOk := series.Labels["topic"]
params := []string{serviceName, topicName}
hashKey := uniqueIdentifier(params, "#")
_, ok := attributeCache.Hash[hashKey]
if topicNameOk && serviceNameOk && !ok {
attributeCache.Hash[hashKey] = struct{}{}
attributeCache.TopicName = append(attributeCache.TopicName, *topicName)
attributeCache.ServiceName = append(attributeCache.ServiceName, *serviceName)
attributeCache.TopicName = append(attributeCache.TopicName, topicName)
attributeCache.ServiceName = append(attributeCache.ServiceName, serviceName)
}
}
}
@@ -3256,23 +3238,25 @@ func (aH *APIHandler) getProducerThroughputOverview(
}
latencyColumn := &v3.Result{QueryName: "latency"}
var latencySeries []*v3.Row
var latencySeries []*v3.Series
for _, res := range resultFetchLatency {
for _, list := range res.List {
topic, topicOk := list.Data["topic"].(*string)
serviceName, serviceNameOk := list.Data["service_name"].(*string)
params := []string{*serviceName, *topic}
for _, series := range res.Series {
topic, topicOk := series.Labels["topic"]
serviceName, serviceNameOk := series.Labels["service_name"]
params := []string{topic, serviceName}
hashKey := uniqueIdentifier(params, "#")
_, ok := attributeCache.Hash[hashKey]
if topicOk && serviceNameOk && ok {
latencySeries = append(latencySeries, list)
latencySeries = append(latencySeries, series)
}
}
}
latencyColumn.List = latencySeries
latencyColumn.Series = latencySeries
result = append(result, latencyColumn)
resultFetchLatency = postprocess.TransformToTableForBuilderQueries(result, queryRangeParams)
resp := v3.QueryRangeResponse{
Result: resultFetchLatency,
}

View File

@@ -284,7 +284,7 @@ func BuildQRParamsWithCache(messagingQueue *MessagingQueue, queryContext string,
cq = &v3.CompositeQuery{
QueryType: v3.QueryTypeBuilder,
BuilderQueries: bhq,
PanelType: v3.PanelTypeList,
PanelType: v3.PanelTypeTable,
}
}
@@ -364,7 +364,7 @@ func BuildClickHouseQuery(messagingQueue *MessagingQueue, queueType string, quer
func buildCompositeQuery(chq *v3.ClickHouseQuery, queryContext string) (*v3.CompositeQuery, error) {
if queryContext == "producer-consumer-eval" || queryContext == "producer-throughput-overview" {
if queryContext == "producer-consumer-eval" {
return &v3.CompositeQuery{
QueryType: v3.QueryTypeClickHouseSQL,
ClickHouseQueries: map[string]*v3.ClickHouseQuery{queryContext: chq},

View File

@@ -0,0 +1,25 @@
package opamp
import "go.signoz.io/signoz/pkg/confmap"
// Config satisfies the confmap.Config interface
var _ confmap.Config = (*Config)(nil)
// Config holds the configuration for http.
type Config struct {
//Address specifies the TCP address for the server to listen on, in the form "host:port".
// If empty, ":http" (port 80) is used. The service names are defined in RFC 6335 and assigned by IANA.
// See net.Dial for details of the address format.
Address string `mapstructure:"address"`
}
func (c *Config) NewWithDefaults() confmap.Config {
return &Config{
Address: "0.0.0.0:4320",
}
}
func (c *Config) Validate() error {
return nil
}

View File

@@ -40,7 +40,6 @@ import (
"go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/dao"
"go.signoz.io/signoz/pkg/query-service/featureManager"
"go.signoz.io/signoz/pkg/query-service/healthcheck"
am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager"
"go.signoz.io/signoz/pkg/query-service/interfaces"
"go.signoz.io/signoz/pkg/query-service/model"
@@ -83,13 +82,6 @@ type Server struct {
privateHTTP *http.Server
opampServer *opamp.Server
unavailableChannel chan healthcheck.Status
}
// HealthCheckStatus returns health check status channel a client can subscribe to
func (s Server) HealthCheckStatus() chan healthcheck.Status {
return s.unavailableChannel
}
// NewServer creates and initializes Server
@@ -118,6 +110,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
readerReady := make(chan bool)
var reader interfaces.Reader
// todo(remove): read from config
storage := os.Getenv("STORAGE")
if storage == "clickhouse" {
zap.L().Info("Using ClickHouse as datastore ...")
@@ -210,9 +203,8 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
s := &Server{
// logger: logger,
// tracer: tracer,
ruleManager: rm,
serverOptions: serverOptions,
unavailableChannel: make(chan healthcheck.Status),
ruleManager: rm,
serverOptions: serverOptions,
}
httpServer, err := s.createPublicServer(apiHandler)
@@ -646,7 +638,6 @@ func (s *Server) Start() error {
default:
zap.L().Error("Could not start HTTP server", zap.Error(err))
}
s.unavailableChannel <- healthcheck.Unavailable
}()
go func() {
@@ -673,9 +664,6 @@ func (s *Server) Start() error {
default:
zap.L().Error("Could not start private HTTP server", zap.Error(err))
}
s.unavailableChannel <- healthcheck.Unavailable
}()
go func() {
@@ -683,7 +671,6 @@ func (s *Server) Start() error {
err := s.opampServer.Start(constants.OpAmpWsEndpoint)
if err != nil {
zap.L().Info("opamp ws server failed to start", zap.Error(err))
s.unavailableChannel <- healthcheck.Unavailable
}
}()

View File

@@ -0,0 +1,23 @@
package auth
import "go.signoz.io/signoz/pkg/confmap"
type Config struct {
Jwt Secret `mapstructure:"jwt"`
}
type Secret struct {
Secret string `mapstructure:"secret"`
}
func (c *Config) NewWithDefaults() confmap.Config {
return &Config{
Jwt: Secret{
Secret: "",
},
}
}
func (c *Config) Validate() error {
return nil
}

32
pkg/query-service/cache/config.go vendored Normal file
View File

@@ -0,0 +1,32 @@
package cache
import (
"time"
"go.signoz.io/signoz/pkg/confmap"
inmemory "go.signoz.io/signoz/pkg/query-service/cache/inmemory"
redis "go.signoz.io/signoz/pkg/query-service/cache/redis"
)
// Config satisfies the confmap.Config interface
var _ confmap.Config = (*Config)(nil)
type Config struct {
Provider string `mapstructure:"provider"`
Redis *redis.Options `yaml:"redis,omitempty"`
Memory *inmemory.Options `yaml:"memory,omitempty"`
}
func (c *Config) NewWithDefaults() confmap.Config {
return &Config{
Provider: "memory",
Memory: &inmemory.Options{
TTL: -1,
CleanupInterval: 1 * time.Minute,
},
}
}
func (c *Config) Validate() error {
return nil
}

View File

@@ -69,9 +69,13 @@ var InviteEmailTemplate = GetOrDefaultEnv("INVITE_EMAIL_TEMPLATE", "/root/templa
// Alert manager channel subpath
var AmChannelApiPath = GetOrDefaultEnv("ALERTMANAGER_API_CHANNEL_PATH", "v1/routes")
// todo(remove): read from config
var OTLPTarget = GetOrDefaultEnv("OTEL_EXPORTER_OTLP_ENDPOINT", "")
// todo(remove): read from config
var LogExportBatchSize = GetOrDefaultEnv("OTEL_BLRP_MAX_EXPORT_BATCH_SIZE", "512")
// todo(remove): read from config
var RELATIONAL_DATASOURCE_PATH = GetOrDefaultEnv("SIGNOZ_LOCAL_DB_PATH", "/var/lib/signoz/signoz.db")
var DurationSortFeature = GetOrDefaultEnv("DURATION_SORT_FEATURE", "true")
@@ -148,6 +152,7 @@ var DEFAULT_FEATURE_SET = model.FeatureSet{
},
}
// todo(remove): read from config
func GetContextTimeout() time.Duration {
contextTimeoutStr := GetOrDefaultEnv("CONTEXT_TIMEOUT", "60")
contextTimeoutDuration, err := time.ParseDuration(contextTimeoutStr + "s")
@@ -157,8 +162,10 @@ func GetContextTimeout() time.Duration {
return contextTimeoutDuration
}
// todo(remove): read from config
var ContextTimeout = GetContextTimeout()
// todo(remove): read from config
func GetContextTimeoutMaxAllowed() time.Duration {
contextTimeoutStr := GetOrDefaultEnv("CONTEXT_TIMEOUT_MAX_ALLOWED", "600")
contextTimeoutDuration, err := time.ParseDuration(contextTimeoutStr + "s")
@@ -177,6 +184,7 @@ func GetEvalDelay() time.Duration {
return evalDelayDuration
}
// todo(remove): read from config
var ContextTimeoutMaxAllowed = GetContextTimeoutMaxAllowed()
const (

View File

@@ -0,0 +1,19 @@
package dao
import "go.signoz.io/signoz/pkg/confmap"
type Config struct {
Provider string `mapstructure:"provider"`
Path string `mapstructure:"path"`
}
func (c *Config) NewWithDefaults() confmap.Config {
return &Config{
Provider: "sqlite",
Path: "/var/lib/signoz.db",
}
}
func (c *Config) Validate() error {
return nil
}

View File

@@ -1,12 +0,0 @@
package healthcheck
const (
// Unavailable indicates the service is not able to handle requests
Unavailable Status = iota
// Ready indicates the service is ready to handle requests
Ready
// Broken indicates that the healthcheck itself is broken, not serving HTTP
Broken
)
type Status int

View File

@@ -1,75 +0,0 @@
package signozio
import (
"encoding/json"
"io"
"net/http"
"time"
"go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/model"
)
var C *Client
const (
POST = "POST"
APPLICATION_JSON = "application/json"
)
type Client struct {
Prefix string
}
func New() *Client {
return &Client{
Prefix: constants.ConfigSignozIo,
}
}
func init() {
C = New()
}
// FetchDynamicConfigs fetches configs from config server
func FetchDynamicConfigs() (map[string]Config, *model.ApiError) {
client := http.Client{Timeout: 5 * time.Second}
req, err := http.NewRequest(http.MethodGet, C.Prefix+"/configs", http.NoBody)
if err != nil {
return DefaultConfig, nil
}
req.SetBasicAuth("admin", "SigNoz@adm1n")
httpResponse, err := client.Do(req)
if err != nil {
return DefaultConfig, nil
}
defer httpResponse.Body.Close()
if err != nil {
return DefaultConfig, nil
}
httpBody, err := io.ReadAll(httpResponse.Body)
if err != nil {
return DefaultConfig, nil
}
// read api request result
result := ConfigResult{}
err = json.Unmarshal(httpBody, &result)
if err != nil {
return DefaultConfig, nil
}
switch httpResponse.StatusCode {
case 200, 201:
return result.Data, nil
case 400, 401:
return DefaultConfig, nil
default:
return DefaultConfig, nil
}
}

View File

@@ -1,54 +0,0 @@
package signozio
type status string
type ConfigResult struct {
Status status `json:"status"`
Data map[string]Config `json:"data,omitempty"`
ErrorType string `json:"errorType,omitempty"`
Error string `json:"error,omitempty"`
}
type Config struct {
Enabled bool `json:"enabled"`
FrontendPositionId string `json:"frontendPositionId"`
Components []ComponentProps `json:"components"`
}
type ComponentProps struct {
Text string `json:"text"`
Position int `json:"position"`
DarkIcon string `json:"darkIcon"`
LightIcon string `json:"lightIcon"`
Href string `json:"href"`
}
var DefaultConfig = map[string]Config{
"helpConfig": {
Enabled: true,
FrontendPositionId: "tooltip",
Components: []ComponentProps{
{
Text: "How to use SigNoz in production",
Position: 1,
LightIcon: "RiseOutlined",
DarkIcon: "RiseOutlined",
Href: "https://signoz.io/docs/production-readiness",
},
{
Text: "Create an issue in GitHub",
Position: 2,
LightIcon: "GithubFilled",
DarkIcon: "GithubOutlined",
Href: "https://github.com/SigNoz/signoz/issues/new/choose",
},
{
Text: "Read the docs",
Position: 3,
LightIcon: "FileTextFilled",
DarkIcon: "FileTextOutlined",
Href: "https://signoz.io/docs",
},
},
},
}

View File

@@ -13,7 +13,6 @@ import (
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/migrate"
"go.signoz.io/signoz/pkg/query-service/version"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -70,7 +69,6 @@ func main() {
defer loggerMgr.Sync() // flushes buffer, if any
logger := loggerMgr.Sugar()
version.PrintVersion()
serverOptions := &app.ServerOptions{
HTTPHostPort: constants.HTTPHostPort,
@@ -89,15 +87,6 @@ func main() {
UseLogsNewSchema: useLogsNewSchema,
}
// Read the jwt secret key
auth.JwtSecret = os.Getenv("SIGNOZ_JWT_SECRET")
if len(auth.JwtSecret) == 0 {
zap.L().Warn("No JWT secret key is specified.")
} else {
zap.L().Info("JWT secret key set successfully.")
}
if err := migrate.Migrate(constants.RELATIONAL_DATASOURCE_PATH); err != nil {
zap.L().Error("Failed to migrate", zap.Error(err))
} else {
@@ -122,8 +111,6 @@ func main() {
for {
select {
case status := <-server.HealthCheckStatus():
logger.Info("Received HealthCheck status: ", zap.Int("status", int(status)))
case <-signalsChannel:
logger.Info("Received OS Interrupt Signal ... ")
err := server.Stop()

View File

@@ -617,7 +617,6 @@ type AlertsInfo struct {
TotalAlerts int `json:"totalAlerts"`
LogsBasedAlerts int `json:"logsBasedAlerts"`
MetricBasedAlerts int `json:"metricBasedAlerts"`
AnomalyBasedAlerts int `json:"anomalyBasedAlerts"`
TracesBasedAlerts int `json:"tracesBasedAlerts"`
TotalChannels int `json:"totalChannels"`
SlackChannels int `json:"slackChannels"`

View File

@@ -42,6 +42,16 @@ var (
// this file contains api request and responses to be
// served over http
// newApiErrorInternal returns a new api error object of type internal
func newApiErrorInternal(err error) *model.ApiError {
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
}
// newApiErrorBadData returns a new api error object of bad request type
func newApiErrorBadData(err error) *model.ApiError {
return &model.ApiError{Typ: model.ErrorBadData, Err: err}
}
// PostableRule is used to create alerting rule from HTTP api
type PostableRule struct {
AlertName string `yaml:"alert,omitempty" json:"alert,omitempty"`

View File

@@ -8,7 +8,7 @@ import (
func TestIsAllQueriesDisabled(t *testing.T) {
testCases := []*v3.CompositeQuery{
{
&v3.CompositeQuery{
BuilderQueries: map[string]*v3.BuilderQuery{
"query1": {
Disabled: true,
@@ -20,10 +20,10 @@ func TestIsAllQueriesDisabled(t *testing.T) {
QueryType: v3.QueryTypeBuilder,
},
nil,
{
&v3.CompositeQuery{
QueryType: v3.QueryTypeBuilder,
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypeBuilder,
BuilderQueries: map[string]*v3.BuilderQuery{
"query1": {
@@ -34,10 +34,10 @@ func TestIsAllQueriesDisabled(t *testing.T) {
},
},
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypePromQL,
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypePromQL,
PromQueries: map[string]*v3.PromQuery{
"query3": {
@@ -45,7 +45,7 @@ func TestIsAllQueriesDisabled(t *testing.T) {
},
},
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypePromQL,
PromQueries: map[string]*v3.PromQuery{
"query3": {
@@ -53,10 +53,10 @@ func TestIsAllQueriesDisabled(t *testing.T) {
},
},
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypeClickHouseSQL,
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypeClickHouseSQL,
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
"query4": {
@@ -64,7 +64,7 @@ func TestIsAllQueriesDisabled(t *testing.T) {
},
},
},
{
&v3.CompositeQuery{
QueryType: v3.QueryTypeClickHouseSQL,
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
"query4": {

View File

@@ -599,9 +599,6 @@ func (r *ruleDB) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) {
}
}
}
if rule.RuleType == RuleTypeAnomaly {
alertsInfo.AnomalyBasedAlerts = alertsInfo.AnomalyBasedAlerts + 1
}
} else if rule.AlertType == AlertTypeTraces {
alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1
}

View File

@@ -10,6 +10,8 @@ import (
"sync"
"time"
"github.com/google/uuid"
"go.uber.org/zap"
"errors"
@@ -22,6 +24,7 @@ import (
"go.signoz.io/signoz/pkg/query-service/model"
pqle "go.signoz.io/signoz/pkg/query-service/pqlEngine"
"go.signoz.io/signoz/pkg/query-service/telemetry"
"go.signoz.io/signoz/pkg/query-service/utils/labels"
)
type PrepareTaskOptions struct {
@@ -38,19 +41,6 @@ type PrepareTaskOptions struct {
UseLogsNewSchema bool
}
type PrepareTestRuleOptions struct {
Rule *PostableRule
RuleDB RuleDB
Logger *zap.Logger
Reader interfaces.Reader
Cache cache.Cache
FF interfaces.FeatureLookup
ManagerOpts *ManagerOptions
NotifyFunc NotifyFunc
UseLogsNewSchema bool
}
const taskNamesuffix = "webAppEditor"
func RuleIdFromTaskName(n string) string {
@@ -91,8 +81,6 @@ type ManagerOptions struct {
PrepareTaskFunc func(opts PrepareTaskOptions) (Task, error)
PrepareTestRuleFunc func(opts PrepareTestRuleOptions) (int, *model.ApiError)
UseLogsNewSchema bool
}
@@ -111,11 +99,10 @@ type Manager struct {
logger *zap.Logger
featureFlags interfaces.FeatureLookup
reader interfaces.Reader
cache cache.Cache
prepareTaskFunc func(opts PrepareTaskOptions) (Task, error)
prepareTestRuleFunc func(opts PrepareTestRuleOptions) (int, *model.ApiError)
featureFlags interfaces.FeatureLookup
reader interfaces.Reader
cache cache.Cache
prepareTaskFunc func(opts PrepareTaskOptions) (Task, error)
UseLogsNewSchema bool
}
@@ -136,9 +123,6 @@ func defaultOptions(o *ManagerOptions) *ManagerOptions {
if o.PrepareTaskFunc == nil {
o.PrepareTaskFunc = defaultPrepareTaskFunc
}
if o.PrepareTestRuleFunc == nil {
o.PrepareTestRuleFunc = defaultTestNotification
}
return o
}
@@ -219,18 +203,17 @@ func NewManager(o *ManagerOptions) (*Manager, error) {
telemetry.GetInstance().SetAlertsInfoCallback(db.GetAlertsInfo)
m := &Manager{
tasks: map[string]Task{},
rules: map[string]Rule{},
notifier: notifier,
ruleDB: db,
opts: o,
block: make(chan struct{}),
logger: o.Logger,
featureFlags: o.FeatureFlags,
reader: o.Reader,
cache: o.Cache,
prepareTaskFunc: o.PrepareTaskFunc,
prepareTestRuleFunc: o.PrepareTestRuleFunc,
tasks: map[string]Task{},
rules: map[string]Rule{},
notifier: notifier,
ruleDB: db,
opts: o,
block: make(chan struct{}),
logger: o.Logger,
featureFlags: o.FeatureFlags,
reader: o.Reader,
cache: o.Cache,
prepareTaskFunc: o.PrepareTaskFunc,
}
return m, nil
}
@@ -805,20 +788,78 @@ func (m *Manager) TestNotification(ctx context.Context, ruleStr string) (int, *m
parsedRule, err := ParsePostableRule([]byte(ruleStr))
if err != nil {
return 0, model.BadRequest(err)
return 0, newApiErrorBadData(err)
}
alertCount, apiErr := m.prepareTestRuleFunc(PrepareTestRuleOptions{
Rule: parsedRule,
RuleDB: m.ruleDB,
Logger: m.logger,
Reader: m.reader,
Cache: m.cache,
FF: m.featureFlags,
ManagerOpts: m.opts,
NotifyFunc: m.prepareNotifyFunc(),
UseLogsNewSchema: m.opts.UseLogsNewSchema,
})
var alertname = parsedRule.AlertName
if alertname == "" {
// alertname is not mandatory for testing, so picking
// a random string here
alertname = uuid.New().String()
}
return alertCount, apiErr
// append name to indicate this is test alert
parsedRule.AlertName = fmt.Sprintf("%s%s", alertname, TestAlertPostFix)
var rule Rule
if parsedRule.RuleType == RuleTypeThreshold {
// add special labels for test alerts
parsedRule.Annotations[labels.AlertSummaryLabel] = fmt.Sprintf("The rule threshold is set to %.4f, and the observed metric value is {{$value}}.", *parsedRule.RuleCondition.Target)
parsedRule.Labels[labels.RuleSourceLabel] = ""
parsedRule.Labels[labels.AlertRuleIdLabel] = ""
// create a threshold rule
rule, err = NewThresholdRule(
alertname,
parsedRule,
m.featureFlags,
m.reader,
m.opts.UseLogsNewSchema,
WithSendAlways(),
WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new threshold rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, newApiErrorBadData(err)
}
} else if parsedRule.RuleType == RuleTypeProm {
// create promql rule
rule, err = NewPromRule(
alertname,
parsedRule,
m.logger,
m.reader,
m.opts.PqlEngine,
WithSendAlways(),
WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new promql rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, newApiErrorBadData(err)
}
} else {
return 0, newApiErrorBadData(fmt.Errorf("failed to derive ruletype with given information"))
}
// set timestamp to current utc time
ts := time.Now().UTC()
count, err := rule.Eval(ctx, ts)
if err != nil {
zap.L().Error("evaluating rule failed", zap.String("rule", rule.Name()), zap.Error(err))
return 0, newApiErrorInternal(fmt.Errorf("rule evaluation failed"))
}
alertsFound, ok := count.(int)
if !ok {
return 0, newApiErrorInternal(fmt.Errorf("something went wrong"))
}
rule.SendAlerts(ctx, ts, 0, time.Duration(1*time.Minute), m.prepareNotifyFunc())
return alertsFound, nil
}

View File

@@ -233,7 +233,6 @@ func AlertTemplateData(labels map[string]string, value string, threshold string)
// consistent across the platform.
// If there is a go template block, it won't be replaced.
// The example for existing go template block is: {{$threshold}} or {{$value}} or any other valid go template syntax.
// See templates_test.go for examples.
func (te *TemplateExpander) preprocessTemplate() {
// Handle the $variable syntax
reDollar := regexp.MustCompile(`({{.*?}})|(\$(\w+(?:\.\w+)*))`)
@@ -257,19 +256,6 @@ func (te *TemplateExpander) preprocessTemplate() {
rest := submatches[2]
return fmt.Sprintf(`{{index .Labels "%s"%s}}`, path, rest)
})
// Handle the {{$variable}} syntax
// skip the special case for {{$threshold}} and {{$value}}
reVariable := regexp.MustCompile(`{{\s*\$\s*([a-zA-Z0-9_.]+)\s*}}`)
te.text = reVariable.ReplaceAllStringFunc(te.text, func(match string) string {
if strings.HasPrefix(match, "{{$threshold}}") || strings.HasPrefix(match, "{{$value}}") {
return match
}
// get the variable name from {{$variable}} syntax
variable := strings.TrimPrefix(match, "{{$")
variable = strings.TrimSuffix(variable, "}}")
return fmt.Sprintf(`{{index .Labels "%s"}}`, variable)
})
}
// Funcs adds the functions in fm to the Expander's function map.
@@ -349,7 +335,6 @@ func (te TemplateExpander) ExpandHTML(templateFiles []string) (result string, re
// ParseTest parses the templates and returns the error if any.
func (te TemplateExpander) ParseTest() error {
te.preprocessTemplate()
_, err := text_template.New(te.name).Funcs(te.funcMap).Option("missingkey=zero").Parse(te.text)
if err != nil {
return err

View File

@@ -74,14 +74,3 @@ func TestTemplateExpander_WithLablesDotSyntax(t *testing.T) {
}
require.Equal(t, "test my-service exceeds 100 and observed at 200", result)
}
func TestTemplateExpander_WithVariableSyntax(t *testing.T) {
defs := "{{$labels := .Labels}}{{$value := .Value}}{{$threshold := .Threshold}}"
data := AlertTemplateData(map[string]string{"service.name": "my-service"}, "200", "100")
expander := NewTemplateExpander(context.Background(), defs+"test {{$service.name}} exceeds {{$threshold}} and observed at {{$value}}", "test", data, times.Time(time.Now().Unix()), nil)
result, err := expander.Expand()
if err != nil {
t.Fatal(err)
}
require.Equal(t, "test my-service exceeds 100 and observed at 200", result)
}

View File

@@ -1,97 +0,0 @@
package rules
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"go.signoz.io/signoz/pkg/query-service/model"
"go.signoz.io/signoz/pkg/query-service/utils/labels"
"go.uber.org/zap"
)
// TestNotification prepares a dummy rule for given rule parameters and
// sends a test notification. returns alert count and error (if any)
func defaultTestNotification(opts PrepareTestRuleOptions) (int, *model.ApiError) {
ctx := context.Background()
if opts.Rule == nil {
return 0, model.BadRequest(fmt.Errorf("rule is required"))
}
parsedRule := opts.Rule
var alertname = parsedRule.AlertName
if alertname == "" {
// alertname is not mandatory for testing, so picking
// a random string here
alertname = uuid.New().String()
}
// append name to indicate this is test alert
parsedRule.AlertName = fmt.Sprintf("%s%s", alertname, TestAlertPostFix)
var rule Rule
var err error
if parsedRule.RuleType == RuleTypeThreshold {
// add special labels for test alerts
parsedRule.Annotations[labels.AlertSummaryLabel] = fmt.Sprintf("The rule threshold is set to %.4f, and the observed metric value is {{$value}}.", *parsedRule.RuleCondition.Target)
parsedRule.Labels[labels.RuleSourceLabel] = ""
parsedRule.Labels[labels.AlertRuleIdLabel] = ""
// create a threshold rule
rule, err = NewThresholdRule(
alertname,
parsedRule,
opts.FF,
opts.Reader,
opts.UseLogsNewSchema,
WithSendAlways(),
WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new threshold rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, model.BadRequest(err)
}
} else if parsedRule.RuleType == RuleTypeProm {
// create promql rule
rule, err = NewPromRule(
alertname,
parsedRule,
opts.Logger,
opts.Reader,
opts.ManagerOpts.PqlEngine,
WithSendAlways(),
WithSendUnmatched(),
)
if err != nil {
zap.L().Error("failed to prepare a new promql rule for test", zap.String("name", rule.Name()), zap.Error(err))
return 0, model.BadRequest(err)
}
} else {
return 0, model.BadRequest(fmt.Errorf("failed to derive ruletype with given information"))
}
// set timestamp to current utc time
ts := time.Now().UTC()
count, err := rule.Eval(ctx, ts)
if err != nil {
zap.L().Error("evaluating rule failed", zap.String("rule", rule.Name()), zap.Error(err))
return 0, model.InternalError(fmt.Errorf("rule evaluation failed"))
}
alertsFound, ok := count.(int)
if !ok {
return 0, model.InternalError(fmt.Errorf("something went wrong"))
}
rule.SendAlerts(ctx, ts, 0, time.Duration(1*time.Minute), opts.NotifyFunc)
return alertsFound, nil
}

View File

@@ -19,7 +19,7 @@ import (
"go.signoz.io/signoz/pkg/query-service/interfaces"
"go.signoz.io/signoz/pkg/query-service/model"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
"go.signoz.io/signoz/pkg/query-service/version"
"go.signoz.io/signoz/pkg/version"
)
const (
@@ -591,7 +591,7 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma
// zap.L().Info(data)
properties := analytics.NewProperties()
properties.Set("version", version.GetVersion())
properties.Set("version", version.Info.Version)
properties.Set("deploymentType", getDeploymentType())
properties.Set("companyDomain", a.getCompanyDomain())

View File

@@ -1,46 +0,0 @@
package version
import (
"fmt"
"runtime"
)
// These fields are set during an official build
// Global vars set from command-line arguments
var (
buildVersion = "--"
buildHash = "--"
buildTime = "--"
gitBranch = "--"
)
// BuildDetails returns a string containing details about the SigNoz query-service binary.
func BuildDetails() string {
licenseInfo := `Check SigNoz Github repo for license details`
return fmt.Sprintf(`
SigNoz version : %v
Commit SHA-1 : %v
Commit timestamp : %v
Branch : %v
Go version : %v
For SigNoz Official Documentation, visit https://signoz.io/docs/
For SigNoz Community Slack, visit http://signoz.io/slack/
For archive of discussions about SigNoz, visit https://knowledgebase.signoz.io/
%s.
Copyright 2024 SigNoz
`,
buildVersion, buildHash, buildTime, gitBranch,
runtime.Version(), licenseInfo)
}
// PrintVersion prints version and other helpful information.
func PrintVersion() {
fmt.Println(BuildDetails())
}
func GetVersion() string {
return buildVersion
}

27
pkg/version/config.go Normal file
View File

@@ -0,0 +1,27 @@
package version
import "go.signoz.io/signoz/pkg/confmap"
// Config satisfies the confmap.Config interface
var _ confmap.Config = (*Config)(nil)
// Config holds the configuration for all instrumentation components.
type Config struct {
Banner Banner `mapstructure:"banner"`
}
type Banner struct {
Enabled bool `mapstructure:"enabled"`
}
func (c *Config) NewWithDefaults() confmap.Config {
return &Config{
Banner: Banner{
Enabled: true,
},
}
}
func (c *Config) Validate() error {
return nil
}

View File

@@ -1,4 +1,3 @@
// Package version is used to track the build information of the application.
// This is typically set via ldflags at build time.
// Eg: -ldflags="-X 'pkg/version.Build.Version=v1.0.0'"
package version

View File

@@ -1,9 +1,136 @@
package version
import (
"fmt"
"runtime"
"strings"
)
// This is set via ldflags at build time.
var (
name string = "-------"
version string = "-------"
hash string = "-------"
time string = "-------"
branch string = "-------"
Info Build = Build{
Name: name,
Version: version,
Hash: hash,
Time: time,
Branch: branch,
GoVersion: runtime.Version(),
}
)
// Build contains information about the build environment.
type Build struct {
// The name of the current build.
Name string
// The version of the current build.
Version string
// The git hash of the current build.
Hash string
// The time of the current build.
Time string
// The branch of the current build.
Branch string
// The version of go.
GoVersion string
}
func (b Build) PrettyPrint() {
ascii := []string{
" -**********= ",
" .::-=+**********+=--:. ",
" .-=*******++=-----==+******=-. ",
" :-+*******=:. :-+******=: ",
" .-********+: .=*******=. ",
" :+********+: .=*******+: ",
" .+*********+ :+***+. -********+: ",
" -**********+. .****= =*********= ",
" .************: +**** +**********: ",
".************+ .----: -***********- ",
"*************= :************.",
":************+ ----: -***********= ",
" :************. ***** +**********: ",
" .=**********+ :****= -*********+. ",
" :+*********+ :+***+ -********+: ",
" :+********+. =*******+- ",
" :=********=. -*******=: ",
" :=*******+-. .-+******=-. ",
" :-+*******+=--:::--=+******+=: ",
" .:-==+***********+=-:: ",
" :**********= ",
}
fields := []struct {
key string
value string
}{
{"Name", b.Name},
{"Version", b.Version},
{"Commit Hash", b.Hash},
{"Commit Time", b.Time},
{"Branch", b.Branch},
{"Go Version", b.GoVersion},
}
maxKeyLength := 0
for _, field := range fields {
if len(field.key) > maxKeyLength {
maxKeyLength = len(field.key)
}
}
maxAsciiWidth := 0
for _, line := range ascii {
if len(line) > maxAsciiWidth {
maxAsciiWidth = len(line)
}
}
panelWidth := maxKeyLength + 30 // Adjust this value to change panel width
fmt.Println()
for i, line := range ascii {
fmt.Print(line)
fmt.Print(" ")
if i == 0 || i == 2 {
fmt.Printf("%s\n", strings.Repeat("-", panelWidth))
continue
}
if i == 1 {
txt := "Starting SigNoz"
fmt.Printf("| %-*s |\n", panelWidth-4, txt)
continue
}
if i-3 >= 0 && i-3 < len(fields) {
field := fields[i-3]
fmt.Printf("| %-*s : %-*s |", maxKeyLength, field.key, panelWidth-maxKeyLength-7, field.value)
} else if i == len(fields)+3 {
fmt.Print(strings.Repeat("-", panelWidth))
} else if i > 2 && i < len(fields)+4 {
fmt.Printf("|%-*s|", panelWidth-2, "")
}
fmt.Println()
}
fmt.Println()
// for i := 0; i < len(ascii); i++ {
// if i < len(fields) {
// fmt.Printf("%-9s %-*s : %s\n", ascii[i], maxKeyLength, fields[i].key, fields[i].value)
// } else {
// fmt.Printf("%-9s\n", ascii[i])
// }
// }
// // Print remaining fields if any
// for i := len(ascii); i < len(fields); i++ {
// fmt.Printf("%9s %-*s : %s\n", "", maxKeyLength, fields[i].key, fields[i].value)
// }
// fmt.Printf("\n")
}