diff --git a/gadb/models.go b/gadb/models.go index a0e9a9f6ef..04af5ee126 100644 --- a/gadb/models.go +++ b/gadb/models.go @@ -945,10 +945,11 @@ type HeartbeatMonitor struct { } type IntegrationKey struct { - ID uuid.UUID - Name string - ServiceID uuid.UUID - Type EnumIntegrationKeysType + ExternalSystemName sql.NullString + ID uuid.UUID + Name string + ServiceID uuid.UUID + Type EnumIntegrationKeysType } type Keyring struct { diff --git a/gadb/queries.sql.go b/gadb/queries.sql.go index 53a7039e58..6bdb5cb4f6 100644 --- a/gadb/queries.sql.go +++ b/gadb/queries.sql.go @@ -1397,15 +1397,16 @@ func (q *Queries) FindOneCalSubForUpdate(ctx context.Context, id uuid.UUID) (Fin } const intKeyCreate = `-- name: IntKeyCreate :exec -INSERT INTO integration_keys(id, name, type, service_id) - VALUES ($1, $2, $3, $4) +INSERT INTO integration_keys(id, name, type, service_id, external_system_name) + VALUES ($1, $2, $3, $4, $5) ` type IntKeyCreateParams struct { - ID uuid.UUID - Name string - Type EnumIntegrationKeysType - ServiceID uuid.UUID + ID uuid.UUID + Name string + Type EnumIntegrationKeysType + ServiceID uuid.UUID + ExternalSystemName sql.NullString } func (q *Queries) IntKeyCreate(ctx context.Context, arg IntKeyCreateParams) error { @@ -1414,6 +1415,7 @@ func (q *Queries) IntKeyCreate(ctx context.Context, arg IntKeyCreateParams) erro arg.Name, arg.Type, arg.ServiceID, + arg.ExternalSystemName, ) return err } @@ -1433,7 +1435,8 @@ SELECT id, name, type, - service_id + service_id, + external_system_name FROM integration_keys WHERE @@ -1441,10 +1444,11 @@ WHERE ` type IntKeyFindByServiceRow struct { - ID uuid.UUID - Name string - Type EnumIntegrationKeysType - ServiceID uuid.UUID + ID uuid.UUID + Name string + Type EnumIntegrationKeysType + ServiceID uuid.UUID + ExternalSystemName sql.NullString } func (q *Queries) IntKeyFindByService(ctx context.Context, serviceID uuid.UUID) ([]IntKeyFindByServiceRow, error) { @@ -1461,6 +1465,7 @@ func (q *Queries) IntKeyFindByService(ctx context.Context, serviceID uuid.UUID) &i.Name, &i.Type, &i.ServiceID, + &i.ExternalSystemName, ); err != nil { return nil, err } @@ -1480,7 +1485,8 @@ SELECT id, name, type, - service_id + service_id, + external_system_name FROM integration_keys WHERE @@ -1488,10 +1494,11 @@ WHERE ` type IntKeyFindOneRow struct { - ID uuid.UUID - Name string - Type EnumIntegrationKeysType - ServiceID uuid.UUID + ID uuid.UUID + Name string + Type EnumIntegrationKeysType + ServiceID uuid.UUID + ExternalSystemName sql.NullString } func (q *Queries) IntKeyFindOne(ctx context.Context, id uuid.UUID) (IntKeyFindOneRow, error) { @@ -1502,6 +1509,7 @@ func (q *Queries) IntKeyFindOne(ctx context.Context, id uuid.UUID) (IntKeyFindOn &i.Name, &i.Type, &i.ServiceID, + &i.ExternalSystemName, ) return i, err } diff --git a/go.mod b/go.mod index 9b6cfdf0e1..81ae9fd24d 100644 --- a/go.mod +++ b/go.mod @@ -101,7 +101,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect github.com/jhump/protoreflect v1.15.3 // indirect diff --git a/go.sum b/go.sum index 0f5b703b8c..4cf4d52065 100644 --- a/go.sum +++ b/go.sum @@ -953,8 +953,9 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= diff --git a/graphql2/generated.go b/graphql2/generated.go index 6d762b6a6e..7be8c43c28 100644 --- a/graphql2/generated.go +++ b/graphql2/generated.go @@ -344,11 +344,12 @@ type ComplexityRoot struct { } IntegrationKey struct { - Href func(childComplexity int) int - ID func(childComplexity int) int - Name func(childComplexity int) int - ServiceID func(childComplexity int) int - Type func(childComplexity int) int + ExternalSystemName func(childComplexity int) int + Href func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + ServiceID func(childComplexity int) int + Type func(childComplexity int) int } IntegrationKeyConnection struct { @@ -2112,6 +2113,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.HeartbeatMonitor.TimeoutMinutes(childComplexity), true + case "IntegrationKey.externalSystemName": + if e.complexity.IntegrationKey.ExternalSystemName == nil { + break + } + + return e.complexity.IntegrationKey.ExternalSystemName(childComplexity), true + case "IntegrationKey.href": if e.complexity.IntegrationKey.Href == nil { break @@ -13540,6 +13548,47 @@ func (ec *executionContext) fieldContext_IntegrationKey_href(ctx context.Context return fc, nil } +func (ec *executionContext) _IntegrationKey_externalSystemName(ctx context.Context, field graphql.CollectedField, obj *integrationkey.IntegrationKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_IntegrationKey_externalSystemName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExternalSystemName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_IntegrationKey_externalSystemName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "IntegrationKey", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _IntegrationKeyConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *IntegrationKeyConnection) (ret graphql.Marshaler) { fc, err := ec.fieldContext_IntegrationKeyConnection_nodes(ctx, field) if err != nil { @@ -13589,6 +13638,8 @@ func (ec *executionContext) fieldContext_IntegrationKeyConnection_nodes(ctx cont return ec.fieldContext_IntegrationKey_name(ctx, field) case "href": return ec.fieldContext_IntegrationKey_href(ctx, field) + case "externalSystemName": + return ec.fieldContext_IntegrationKey_externalSystemName(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type IntegrationKey", field.Name) }, @@ -16071,6 +16122,8 @@ func (ec *executionContext) fieldContext_Mutation_createIntegrationKey(ctx conte return ec.fieldContext_IntegrationKey_name(ctx, field) case "href": return ec.fieldContext_IntegrationKey_href(ctx, field) + case "externalSystemName": + return ec.fieldContext_IntegrationKey_externalSystemName(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type IntegrationKey", field.Name) }, @@ -19298,6 +19351,8 @@ func (ec *executionContext) fieldContext_Query_integrationKey(ctx context.Contex return ec.fieldContext_IntegrationKey_name(ctx, field) case "href": return ec.fieldContext_IntegrationKey_href(ctx, field) + case "externalSystemName": + return ec.fieldContext_IntegrationKey_externalSystemName(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type IntegrationKey", field.Name) }, @@ -24841,6 +24896,8 @@ func (ec *executionContext) fieldContext_Service_integrationKeys(ctx context.Con return ec.fieldContext_IntegrationKey_name(ctx, field) case "href": return ec.fieldContext_IntegrationKey_href(ctx, field) + case "externalSystemName": + return ec.fieldContext_IntegrationKey_externalSystemName(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type IntegrationKey", field.Name) }, @@ -31796,7 +31853,7 @@ func (ec *executionContext) unmarshalInputCreateIntegrationKeyInput(ctx context. asMap[k] = v } - fieldsInOrder := [...]string{"serviceID", "type", "name"} + fieldsInOrder := [...]string{"serviceID", "type", "name", "externalSystemName"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -31824,6 +31881,13 @@ func (ec *executionContext) unmarshalInputCreateIntegrationKeyInput(ctx context. return it, err } it.Name = data + case "externalSystemName": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("externalSystemName")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.ExternalSystemName = data } } @@ -37727,6 +37791,8 @@ func (ec *executionContext) _IntegrationKey(ctx context.Context, sel ast.Selecti } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "externalSystemName": + out.Values[i] = ec._IntegrationKey_externalSystemName(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -47932,6 +47998,16 @@ func (ec *executionContext) unmarshalOSlackUserGroupSearchOptions2ᚖgithubᚗco return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + return res +} + func (ec *executionContext) unmarshalOString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { if v == nil { return nil, nil diff --git a/graphql2/graphqlapp/integrationkey.go b/graphql2/graphqlapp/integrationkey.go index e668fb3e70..2213569002 100644 --- a/graphql2/graphqlapp/integrationkey.go +++ b/graphql2/graphqlapp/integrationkey.go @@ -29,6 +29,9 @@ func (m *Mutation) CreateIntegrationKey(ctx context.Context, input graphql2.Crea Name: input.Name, Type: integrationkey.Type(input.Type), } + if input.ExternalSystemName != nil { + key.ExternalSystemName = *input.ExternalSystemName + } key, err = m.IntKeyStore.Create(ctx, tx, key) return err }) diff --git a/graphql2/models_gen.go b/graphql2/models_gen.go index fa6fd02d02..ac2445f454 100644 --- a/graphql2/models_gen.go +++ b/graphql2/models_gen.go @@ -184,6 +184,8 @@ type CreateIntegrationKeyInput struct { ServiceID *string `json:"serviceID,omitempty"` Type IntegrationKeyType `json:"type"` Name string `json:"name"` + // Name of the external system this key is managed by. + ExternalSystemName *string `json:"externalSystemName,omitempty"` } type CreateRotationInput struct { diff --git a/graphql2/schema.graphql b/graphql2/schema.graphql index 4d3816b11b..284f92fab7 100644 --- a/graphql2/schema.graphql +++ b/graphql2/schema.graphql @@ -1000,6 +1000,11 @@ input CreateIntegrationKeyInput { serviceID: ID type: IntegrationKeyType! name: String! + + """ + Name of the external system this key is managed by. + """ + externalSystemName: String } input CreateHeartbeatMonitorInput { @@ -1044,6 +1049,11 @@ type IntegrationKey { type: IntegrationKeyType! name: String! href: String! + + """ + Name of the external system this key is managed by. + """ + externalSystemName: String } enum IntegrationKeyType { diff --git a/integrationkey/integrationkey.go b/integrationkey/integrationkey.go index 90590e1f89..13ea122788 100644 --- a/integrationkey/integrationkey.go +++ b/integrationkey/integrationkey.go @@ -9,6 +9,8 @@ type IntegrationKey struct { Name string `json:"name"` Type Type `json:"type"` ServiceID string `json:"service_id"` + + ExternalSystemName string } func (i IntegrationKey) Normalize() (*IntegrationKey, error) { @@ -16,6 +18,7 @@ func (i IntegrationKey) Normalize() (*IntegrationKey, error) { validate.IDName("Name", i.Name), validate.UUID("ServiceID", i.ServiceID), validate.OneOf("Type", i.Type, TypeGrafana, TypeSite24x7, TypePrometheusAlertmanager, TypeGeneric, TypeEmail), + validate.ASCII("ExternalSystemName", i.ExternalSystemName, 0, 255), ) if err != nil { return nil, err diff --git a/integrationkey/queries.sql b/integrationkey/queries.sql index 046443d702..e39cbe1e76 100644 --- a/integrationkey/queries.sql +++ b/integrationkey/queries.sql @@ -8,15 +8,16 @@ WHERE AND type = $2; -- name: IntKeyCreate :exec -INSERT INTO integration_keys(id, name, type, service_id) - VALUES ($1, $2, $3, $4); +INSERT INTO integration_keys(id, name, type, service_id, external_system_name) + VALUES ($1, $2, $3, $4, $5); -- name: IntKeyFindOne :one SELECT id, name, type, - service_id + service_id, + external_system_name FROM integration_keys WHERE @@ -27,7 +28,8 @@ SELECT id, name, type, - service_id + service_id, + external_system_name FROM integration_keys WHERE diff --git a/integrationkey/store.go b/integrationkey/store.go index 30bea03e8e..8ee2908d03 100644 --- a/integrationkey/store.go +++ b/integrationkey/store.go @@ -92,6 +92,8 @@ func (s *Store) Create(ctx context.Context, dbtx gadb.DBTX, i *IntegrationKey) ( Name: n.Name, Type: gadb.EnumIntegrationKeysType(n.Type), ServiceID: serviceUUID, + + ExternalSystemName: sql.NullString{String: n.ExternalSystemName, Valid: n.ExternalSystemName != ""}, }) if err != nil { return nil, err @@ -142,6 +144,8 @@ func (s *Store) FindOne(ctx context.Context, id string) (*IntegrationKey, error) Name: row.Name, Type: Type(row.Type), ServiceID: row.ServiceID.String(), + + ExternalSystemName: row.ExternalSystemName.String, }, nil } @@ -167,6 +171,8 @@ func (s *Store) FindAllByService(ctx context.Context, serviceID string) ([]Integ Name: row.Name, Type: Type(row.Type), ServiceID: row.ServiceID.String(), + + ExternalSystemName: row.ExternalSystemName.String, } } return keys, nil diff --git a/migrate/migrations/20240415143348-ext-int-keys.sql b/migrate/migrations/20240415143348-ext-int-keys.sql new file mode 100644 index 0000000000..67b5b521e5 --- /dev/null +++ b/migrate/migrations/20240415143348-ext-int-keys.sql @@ -0,0 +1,23 @@ +-- +migrate Up +ALTER TABLE integration_keys + ADD COLUMN external_system_name TEXT, + DROP CONSTRAINT integration_keys_name_service_id_key; + +DROP INDEX IF EXISTS integration_keys_name_service_id_key; + +DROP INDEX IF EXISTS integration_keys_name_service_id; + +CREATE UNIQUE INDEX idx_int_key_name_svc_ext ON public.integration_keys USING btree(lower(name), service_id, coalesce(external_system_name, '')); + +-- +migrate Down +LOCK TABLE integration_keys; + +DELETE FROM integration_keys +WHERE external_system_name IS NOT NULL; + +ALTER TABLE integration_keys + DROP COLUMN external_system_name, + ADD CONSTRAINT integration_keys_name_service_id_key UNIQUE (name, service_id); + +CREATE UNIQUE INDEX integration_keys_name_service_id ON public.integration_keys USING btree(lower(name), service_id); + diff --git a/migrate/schema.sql b/migrate/schema.sql index 1d3f64d2b2..06c1c5dcd1 100644 --- a/migrate/schema.sql +++ b/migrate/schema.sql @@ -1,7 +1,7 @@ -- This file is auto-generated by "make db-schema"; DO NOT EDIT --- DATA=80b70842c372137de893fdbeb2e15ff063e281350f99ac81b71965ba0b8ea167 - --- DISK=d28a06c3676164b926f7998045b6ad7c986cab75cd3e8cdbd98d4991fdcce593 - --- PSQL=d28a06c3676164b926f7998045b6ad7c986cab75cd3e8cdbd98d4991fdcce593 - +-- DATA=75b49324476222eb037d3cba6e07d309090a82b93508ac3e72d9908279657a33 - +-- DISK=e74e9237489fd549f592dca04bb8781e83c9923a5d594b24709b6496990b385a - +-- PSQL=e74e9237489fd549f592dca04bb8781e83c9923a5d594b24709b6496990b385a - -- -- pgdump-lite database dump -- @@ -1675,18 +1675,17 @@ CREATE CONSTRAINT TRIGGER trg_enforce_heartbeat_monitor_limit AFTER INSERT ON pu CREATE TABLE integration_keys ( + external_system_name text, id uuid DEFAULT gen_random_uuid() NOT NULL, name text NOT NULL, service_id uuid NOT NULL, type enum_integration_keys_type NOT NULL, - CONSTRAINT integration_keys_name_service_id_key UNIQUE (name, service_id), CONSTRAINT integration_keys_pkey PRIMARY KEY (id), CONSTRAINT integration_keys_services_id_fkey FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE ); +CREATE UNIQUE INDEX idx_int_key_name_svc_ext ON public.integration_keys USING btree (lower(name), service_id, COALESCE(external_system_name, ''::text)); CREATE INDEX idx_integration_key_service ON public.integration_keys USING btree (service_id); -CREATE UNIQUE INDEX integration_keys_name_service_id ON public.integration_keys USING btree (lower(name), service_id); -CREATE UNIQUE INDEX integration_keys_name_service_id_key ON public.integration_keys USING btree (name, service_id); CREATE UNIQUE INDEX integration_keys_pkey ON public.integration_keys USING btree (id); CREATE CONSTRAINT TRIGGER trg_enforce_integration_key_limit AFTER INSERT ON public.integration_keys NOT DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE FUNCTION fn_enforce_integration_key_limit(); diff --git a/package.json b/package.json index 24aa349940..7aa5f69b32 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "@material/material-color-utilities": "0.2.7", "@mui/icons-material": "5.15.13", "@mui/lab": "5.0.0-alpha.162", - "@mui/material": "5.15.5", - "@mui/styles": "5.15.5", + "@mui/material": "5.15.15", + "@mui/styles": "5.15.15", "@mui/system": "5.15.6", "@mui/x-data-grid": "6.19.6", "@playwright/test": "1.41.2", @@ -58,7 +58,7 @@ "@storybook/test": "8.0.8", "@storybook/test-runner": "0.17.0", "@storybook/types": "8.0.6", - "@types/chance": "1.1.4", + "@types/chance": "1.1.6", "@types/diff": "5.0.8", "@types/glob": "8.1.0", "@types/jest": "29.5.12", @@ -71,7 +71,7 @@ "@types/react-dom": "18.2.22", "@types/react-transition-group": "4.4.10", "@types/react-virtualized-auto-sizer": "1.0.4", - "@typescript-eslint/eslint-plugin": "7.1.0", + "@typescript-eslint/eslint-plugin": "7.7.0", "@typescript-eslint/parser": "6.21.0", "@urql/exchange-retry": "1.2.1", "bowser": "2.11.0", @@ -125,7 +125,7 @@ "react-redux": "8.1.3", "react-transition-group": "4.4.5", "react-virtualized-auto-sizer": "1.0.20", - "recharts": "2.8.0", + "recharts": "2.9.2", "redux": "4.2.1", "redux-devtools-extension": "2.13.9", "redux-thunk": "2.4.2", diff --git a/test/smoke/migrations_test.go b/test/smoke/migrations_test.go index 5cfd10cb8f..c5c5ebefc9 100644 --- a/test/smoke/migrations_test.go +++ b/test/smoke/migrations_test.go @@ -1,16 +1,12 @@ package smoke import ( - "bufio" "bytes" "context" "database/sql" "fmt" "math/rand" "os" - "os/exec" - "regexp" - "sort" "strings" "testing" "text/template" @@ -18,20 +14,19 @@ import ( "github.com/google/uuid" _ "github.com/jackc/pgx/v5/stdlib" // import db driver + "github.com/stretchr/testify/require" "github.com/target/goalert/migrate" "github.com/target/goalert/test/smoke/harness" + "github.com/target/goalert/test/smoke/migratetest" "github.com/target/goalert/util/sqlutil" ) -type ignoreRule struct { - MigrationName string - TableName string - ColumnName string - ExtraRows bool - MissingRows bool -} +// DefaultSkipToMigration is the default migration to skip to when running the migration tests. +// +// It can be overriden by setting the SKIP_TO environment variable. +const DefaultSkipToMigration = "switchover-mk2" -var ignoreRules = []ignoreRule{ +var rules = migratetest.RuleSet{ // All migration timestamps will differ as they applied/re-applied {TableName: "gorp_migrations", ColumnName: "applied_at"}, @@ -162,283 +157,6 @@ values ({{uuid "cb2"}}, {{uuid "ncy1"}}, 1, {{uuid "c2"}}, {{uuid "n2"}}, now()); ` -type pgDumpEntry struct { - Name string - Body string -} - -var enumRx = regexp.MustCompile(`(?s)CREATE TYPE ([\w_.]+) AS ENUM \(\s*(.*)\s*\);`) - -// enumOK handles checking for safe enum differences. This case is that migrate -// up can add, but migrate down will not remove new enum values. -// -// migrate down can't safely remove enum values, but it's safe for new ones -// to exist. So we simply check that all original items exist. -func enumOK(got, want string) bool { - partsW := enumRx.FindStringSubmatch(want) - if len(partsW) != 3 { - return false - } - partsG := enumRx.FindStringSubmatch(got) - if len(partsG) != 3 { - return false - } - if partsW[1] != partsG[1] { - return false - } - - gotItems := strings.Split(partsG[2], ",\n") - wantItems := strings.Split(partsW[2], ",\n") - - g := make(map[string]bool, len(gotItems)) - for _, v := range gotItems { - g[strings.TrimSpace(v)] = true - } - - for _, v := range wantItems { - if !g[strings.TrimSpace(v)] { - return false - } - } - - return true -} - -func TestEnumOK(t *testing.T) { - const got = `CREATE TYPE enum_alert_log_event AS ENUM ( -'created', -'reopened', -'status_changed', -'assignment_changed', -'escalated', -'closed', -'notification_sent', -'response_received', -'acknowledged', -'policy_updated', -'duplicate_suppressed', -'escalation_request' -);` - const want = `CREATE TYPE enum_alert_log_event AS ENUM ( -'created', -'reopened', -'status_changed', -'assignment_changed', -'escalated', -'closed', -'notification_sent', -'response_received' -);` - - if !enumOK(got, want) { - t.Errorf("got false; want true") - } -} - -func processIgnoreRules(ignoreRules []ignoreRule, name, body string) string { - for _, r := range ignoreRules { - if r.MigrationName != "" && r.MigrationName != name { - continue - } - if !strings.HasPrefix(body, "COPY "+r.TableName+" ") && !strings.HasPrefix(body, "COPY public."+r.TableName+" ") { - continue - } - lines := strings.Split(body, "\n") - pref, cols, suf := getCols(lines[0]) - index := -1 - for i, v := range cols { - if v == r.ColumnName { - index = i - } - } - if index == -1 { - continue - } - newLen := len(cols) - 1 - copy(cols[index:], cols[index+1:]) - cols = cols[:newLen] - lines[0] = pref + strings.Join(cols, ", ") + suf - - data := lines[1 : len(lines)-1] - for i, l := range data { - cols = strings.Split(l, "\t") - copy(cols[index:], cols[index+1:]) - cols = cols[:newLen] - data[i] = strings.Join(cols, "\t") - } - body = strings.Join(lines, "\n") - } - return body -} - -func TestProcessIgnoreRules(t *testing.T) { - t.Parallel() - const input = `COPY public.my_table (foo, bar, baz) FROM stdin; -1 2 3 -a b c -\.` - const expected = `COPY public.my_table (foo, baz) FROM stdin; -1 3 -a c -\.` - rules := []ignoreRule{ - {MigrationName: "foo", TableName: "my_table", ColumnName: "bar"}, - } - result := processIgnoreRules(rules, "foo", input) - if result != expected { - t.Errorf("got\n%s\n\nwant\n%s", result, expected) - } -} - -func getCols(line string) (prefix string, cols []string, suffix string) { - cols = strings.SplitN(line, "(", 2) - - prefix = cols[0] + "(" - suffix = cols[1] - cols = strings.SplitN(suffix, ")", 2) - suffix = ")" + cols[1] - cols = strings.Split(cols[0], ", ") - - return prefix, cols, suffix -} - -func alphabetizeCopy(body string) string { - lines := strings.Split(body, "\n") - data := lines[1 : len(lines)-1] - - pref, cols, suf := getCols(lines[0]) - - orig := make(map[string]int, len(cols)) - for i, c := range cols { - orig[c] = i - } - sort.Strings(cols) - lines[0] = pref + strings.Join(cols, ", ") + suf - - order := make(map[int]int, len(cols)) - for i, c := range cols { - order[orig[c]] = i - } - - for n, l := range data { - cols = strings.Split(l, "\t") - sorted := make([]string, len(cols)) - for i, v := range cols { - sorted[order[i]] = v - } - - data[n] = strings.Join(sorted, "\t") - } - - sort.Strings(lines[1:]) - return strings.Join(lines, "\n") -} - -func TestAlphabetizeCopy(t *testing.T) { - t.Parallel() - const input = `COPY foobar (a, e, f, b, c) FROM stdin; -first second third fourth fifth -\.` - const expected = `COPY foobar (a, b, c, e, f) FROM stdin; -\. -first fourth fifth second third` - result := alphabetizeCopy(input) - if result != expected { - t.Errorf("got\n%s\n\nwant\n%s", result, expected) - } -} - -func parsePGDump(data []byte, name string) []pgDumpEntry { - rd := bufio.NewReader(bytes.NewReader(data)) - - entries := make([]pgDumpEntry, 0, 10000) - var entry pgDumpEntry - - addEntry := func() { - if strings.Contains(entry.Body, "COPY notifications (user_id, started_at) FROM stdin") { - // we ignore the (old) notifications table - // since it's trigger based and always re-calculated - // - // which makes it near impossible to test migrations - // - // it also doesn't work properly anyhow, which is why it has been - // replaced. - return - } - - entry.Body = strings.TrimSpace(entry.Body) - entry.Name = strings.TrimSpace(entry.Name) - - if strings.HasPrefix(entry.Body, "COPY ") && strings.Contains(entry.Name, "Type: TABLE DATA") { - // ignore column order, as long as the data matches - entry.Body = processIgnoreRules(ignoreRules, name, entry.Body) - entry.Body = alphabetizeCopy(entry.Body) - } - if strings.Contains(entry.Body, "REPLICA IDENTITY NOTHING") && strings.Contains(entry.Body, "ALTER TABLE ONLY") { - // skip 'view' tables - return - } - - if strings.Contains(entry.Name, " _RETURN; Type: RULE") { - // view return rule -> convert to view - tname := strings.SplitN(entry.Name, " ", 2)[0] - entry.Name = strings.Replace(entry.Name, " _RETURN; Type: RULE", "; Type: VIEW", 1) - entry.Body = strings.Replace(entry.Body, "CREATE RULE \"_RETURN\" AS\n", "", 1) - entry.Body = strings.Replace(entry.Body, - "ON SELECT TO "+tname+" DO INSTEAD ", - "CREATE VIEW "+tname+" AS\n", - 1, - ) - } - - if strings.HasPrefix(entry.Body, "CREATE TABLE") { - // order args alphabetically - lines := strings.Split(entry.Body, "\n") - sort.Strings(lines[1 : len(lines)-1]) - for i := 1; i < len(lines)-1; i++ { - if !strings.HasSuffix(lines[i], ",") { - lines[i] += "," - } - } - entry.Body = strings.Join(lines, "\n") - } - - entries = append(entries, entry) - } - - for { - line, err := rd.ReadString('\n') - if err != nil { - break - } - if strings.HasPrefix(line, "-- Name: ") { - entry.Name = strings.TrimSpace(strings.TrimPrefix(line, "-- Name: ")) - entry.Body = "" - _, _ = rd.ReadString('\n') // skip next line - continue - } else if strings.HasPrefix(line, "-- Data for Name: ") { - entry.Name = strings.TrimSpace(strings.TrimPrefix(line, "-- Data for Name: ")) - entry.Body = "" - _, _ = rd.ReadString('\n') // skip next line - continue - } else if strings.HasPrefix(line, "--") { - if entry.Name != "" { - addEntry() - } - entry.Body = "" - entry.Name = "" - } else if entry.Name != "" && line != "" { - entry.Body += strings.Trim(line, "\n ") + "\n" - } - } - - return entries -} - -func indent(str string) string { - return " " + strings.Replace(str, "\n", "\n ", -1) -} - // https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") @@ -480,57 +198,6 @@ func renderQuery(t *testing.T, sql string) string { return b.String() } -func (e pgDumpEntry) matchesBody(migrationName string, body string) bool { - if e.Body == body { - return true - } - if enumOK(body, e.Body) { - return true - } - - if !strings.HasPrefix(e.Body, "COPY ") { - return false - } - - // check for extra rows rule - var extraRows, missingRows bool - for _, r := range ignoreRules { - if r.MigrationName != migrationName { - continue - } - if !strings.HasPrefix(e.Name, r.TableName+";") { - continue - } - extraRows = extraRows || r.ExtraRows - missingRows = missingRows || r.MissingRows - } - if !extraRows && !missingRows { - return false - } - - e.Body = strings.TrimSuffix(e.Body, "\n\\.") - body = strings.TrimSuffix(body, "\n\\.") - if extraRows { - rows := strings.Split(body, "\n") - for i := range rows { - if e.Body == strings.Join(rows[:len(rows)-i], "\n") { - return true - } - } - } - - if missingRows { - rows := strings.Split(e.Body, "\n") - for i := range rows { - if body == strings.Join(rows[:len(rows)-i], "\n") { - return true - } - } - } - - return false -} - func TestMigrations(t *testing.T) { if testing.Short() { t.Skip("skipping migrations tests for short mode") @@ -546,20 +213,22 @@ func TestMigrations(t *testing.T) { defer db.Close() dbName := strings.Replace("migrations_smoketest_"+time.Now().Format("2006_01_02_03_04_05")+uuid.New().String(), "-", "", -1) + testURL := harness.DBURL(dbName) + _, err = db.Exec("create database " + sqlutil.QuoteID(dbName)) if err != nil { t.Fatal("failed to create db:", err) } defer func() { _, _ = db.Exec("drop database " + sqlutil.QuoteID(dbName)) }() - n, err := migrate.Up(context.Background(), harness.DBURL(dbName), start) + n, err := migrate.Up(context.Background(), testURL, start) if err != nil { t.Fatal("failed to apply initial migrations:", err) } initSQL := renderQuery(t, migrateInitData) - err = harness.ExecSQLBatch(context.Background(), harness.DBURL(dbName), initSQL) + err = harness.ExecSQLBatch(context.Background(), testURL, initSQL) if err != nil { t.Fatalf("failed to init db %v", err) } @@ -571,7 +240,7 @@ func TestMigrations(t *testing.T) { start = env skipTo = true } else { - start = "switchover-mk2" // default skip_to + start = DefaultSkipToMigration skipTo = true } var idx int @@ -583,7 +252,7 @@ func TestMigrations(t *testing.T) { names = names[idx:] if skipTo { - n, err := migrate.Up(context.Background(), harness.DBURL(dbName), start) + n, err := migrate.Up(context.Background(), testURL, start) if err != nil { t.Fatal("failed to apply skip migrations:", err) } @@ -593,92 +262,60 @@ func TestMigrations(t *testing.T) { t.Logf("Skipping to %s", start) } - snapshot := func(t *testing.T, name string) []pgDumpEntry { - data, err := exec.Command("pg_dump", - "-d", harness.DBURL(dbName), - "-O", - ).Output() - if err != nil { - t.Fatal("failed to dump db:", err) - } - return parsePGDump(data, name) - } - mm := 0 - checkDiff := func(t *testing.T, typ, migrationName string, a, b []pgDumpEntry) bool { - m1 := make(map[string]string) - m2 := make(map[string]string) - for _, e := range a { - m1[e.Name] = e.Body - } - for _, e := range b { - m2[e.Name] = e.Body - } - var mismatch bool - for _, e := range a { - body, ok := m2[e.Name] - if !ok { - mismatch = true - t.Errorf("%s missing\n%s\n%s", typ, e.Name, indent(e.Body)) - continue - } - if !e.matchesBody(migrationName, body) { - mismatch = true - t.Errorf("%s mismatch\n%s\ngot\n%s\nwant\n%s", typ, e.Name, indent(body), indent(e.Body)) - continue - } - } - for _, e := range b { - _, ok := m1[e.Name] - if !ok { - mismatch = true - t.Errorf("%s leftover\n%s\n%s", typ, e.Name, indent(e.Body)) - } - } + snapshot := func(t *testing.T, name string) *migratetest.Snapshot { + t.Helper() - mm++ - return mismatch + snap, err := migratetest.NewSnapshotURL(context.Background(), testURL) + require.NoErrorf(t, err, "failed to create snapshot for %s", name) + return snap } + names = names[1:] for i, migrationName := range names[1:] { lastMigrationName := names[i] - var applied bool + var beforeUpSnap *migratetest.Snapshot pass := t.Run(migrationName, func(t *testing.T) { ctx := context.Background() - orig := snapshot(t, migrationName) - n, err = migrate.Up(ctx, harness.DBURL(dbName), migrationName) - if err != nil { - t.Fatalf("failed to apply UP migration: %v", err) + + if beforeUpSnap == nil { + beforeUpSnap = snapshot(t, migrationName) } + + n, err = migrate.Up(ctx, testURL, migrationName) + require.NoError(t, err, "failed to apply UP migration") if n == 0 { + // no more migrations are left, so end the test return } - applied = true - upSnap := snapshot(t, migrationName) - _, err = migrate.Down(ctx, harness.DBURL(dbName), lastMigrationName) - if err != nil { - t.Fatalf("failed to apply DOWN migration: %v", err) - } - applied = false - s := snapshot(t, migrationName) - if checkDiff(t, "DOWN", migrationName, orig, s) { - t.Fatalf("DOWN migration did not restore previous schema") - } - _, err = migrate.Up(ctx, harness.DBURL(dbName), migrationName) - if err != nil { - t.Fatalf("failed to apply UP migration (2nd time): %v", err) + afterUpSnap1 := snapshot(t, migrationName) + + _, err = migrate.Down(ctx, testURL, lastMigrationName) + require.NoError(t, err, "failed to apply DOWN migration") + + afterDownSnap := snapshot(t, migrationName) + pass := t.Run("Down", func(t *testing.T) { + rules.RequireEqualDown(t, beforeUpSnap, afterDownSnap) + }) + if !pass { + return } - applied = true - s = snapshot(t, migrationName) - if checkDiff(t, "UP", migrationName, upSnap, s) { - t.Fatalf("UP migration did not restore previous schema") + + _, err = migrate.Up(ctx, testURL, migrationName) + require.NoError(t, err, "failed to apply UP migration (2nd time)") + + afterUpSnap2 := snapshot(t, migrationName) + pass = t.Run("Up", func(t *testing.T) { + rules.RequireEqualUp(t, afterUpSnap1, afterUpSnap2) + }) + if !pass { + return } + + beforeUpSnap = afterUpSnap2 // save for next iteration }) - if !pass && !applied { - n, err = migrate.Up(context.Background(), harness.DBURL(dbName), migrationName) - if err != nil || n == 0 { - t.Fatalf("failed to apply UP migration; abort") - } + if !pass { + return } } } diff --git a/util/errutil/maperror.go b/util/errutil/maperror.go index 9d7cec01b3..eb0bcaee8c 100644 --- a/util/errutil/maperror.go +++ b/util/errutil/maperror.go @@ -43,6 +43,9 @@ func MapDBError(err error) error { return validation.NewFieldError("UserID", "user does not exist") } case "23505": // unique constraint + if dbErr.ConstraintName == "idx_int_key_name_svc_ext" { + return validation.NewFieldError("Name", "already in use") + } if dbErr.ConstraintName == "auth_basic_users_username_key" { return validation.NewFieldError("Username", "already in use") } diff --git a/web/src/app/services/IntegrationKeyDeleteDialog.tsx b/web/src/app/services/IntegrationKeyDeleteDialog.tsx index d87faa0d2a..ec7d193f4b 100644 --- a/web/src/app/services/IntegrationKeyDeleteDialog.tsx +++ b/web/src/app/services/IntegrationKeyDeleteDialog.tsx @@ -2,9 +2,14 @@ import React from 'react' import { gql, useQuery, useMutation } from 'urql' import { nonFieldErrors } from '../util/errutil' -import Spinner from '../loading/components/Spinner' import { GenericError } from '../error-pages' import FormDialog from '../dialogs/FormDialog' +import { + Checkbox, + FormControl, + FormControlLabel, + FormHelperText, +} from '@mui/material' const query = gql` query ($id: ID!) { @@ -12,6 +17,7 @@ const query = gql` id name serviceID + externalSystemName } } ` @@ -26,17 +32,19 @@ export default function IntegrationKeyDeleteDialog(props: { integrationKeyID: string onClose: () => void }): JSX.Element { - const [{ fetching, error, data }] = useQuery({ + const [{ error, data }] = useQuery({ query, variables: { id: props.integrationKeyID }, }) + const extSystemName = data?.integrationKey?.externalSystemName || '' + const [confirmed, setConfirmed] = React.useState(!extSystemName) // only require confirmation if external system name is present + const [confirmError, setConfirmError] = React.useState(false) const [deleteKeyStatus, deleteKey] = useMutation(mutation) - if (fetching && !data) return if (error) return - if (!fetching && !deleteKeyStatus.fetching && data?.integrationKey === null) { + if (!data?.integrationKey) { return ( + { + setConfirmed(e.target.checked) + setConfirmError(false) + }} + /> + } + label='I understand the consequences of deleting this key' + /> + + {confirmError ? ( + 'Please confirm' + ) : ( + + Deleting this key may break integrations with  + {extSystemName} + + )} + + + ) + } + return ( { + if (!confirmed) { + setConfirmError(true) + return + } + setConfirmError(false) + const input = [ { type: 'integrationKey', diff --git a/web/src/app/services/IntegrationKeyList.tsx b/web/src/app/services/IntegrationKeyList.tsx index 8bcaf6ba41..fa97a27247 100644 --- a/web/src/app/services/IntegrationKeyList.tsx +++ b/web/src/app/services/IntegrationKeyList.tsx @@ -13,12 +13,20 @@ import IntegrationKeyDeleteDialog from './IntegrationKeyDeleteDialog' import CopyText from '../util/CopyText' import AppLink from '../util/AppLink' import { useIsWidthDown } from '../util/useWidth' -import { Add } from '@mui/icons-material' +import { Add, ArrowDownward } from '@mui/icons-material' import makeStyles from '@mui/styles/makeStyles' import Spinner from '../loading/components/Spinner' import { GenericError } from '../error-pages' import { IntegrationKey } from '../../schema' import { useFeatures } from '../util/RequireConfig' +import { + Accordion, + AccordionDetails, + AccordionSummary, + Chip, + Divider, + Typography, +} from '@mui/material' const query = gql` query ($serviceID: ID!) { @@ -29,6 +37,7 @@ const query = gql` type name href + externalSystemName } } } @@ -50,6 +59,12 @@ const useStyles = makeStyles({ }) const sortItems = (a: IntegrationKey, b: IntegrationKey): number => { + const extA = a.externalSystemName || '' + const extB = b.externalSystemName || '' + if (extA.toLowerCase() < extB.toLowerCase()) return -1 + if (extA.toLowerCase() > extB.toLowerCase()) return 1 + if (extA < extB) return -1 + if (extA > extB) return 1 if (a.name.toLowerCase() < b.name.toLowerCase()) return -1 if (a.name.toLowerCase() > b.name.toLowerCase()) return 1 if (a.name < b.name) return -1 @@ -101,6 +116,7 @@ export default function IntegrationKeyList(props: { const items = (data.service.integrationKeys || []) .slice() .sort(sortItems) + .filter((key: IntegrationKey) => !key.externalSystemName) .map( (key: IntegrationKey): FlatListListItem => ({ title: key.name, @@ -123,6 +139,25 @@ export default function IntegrationKeyList(props: { }), ) + const extItems = (data.service.integrationKeys || []) + .slice() + .sort(sortItems) + .filter((key: IntegrationKey) => !!key.externalSystemName) + .map( + (key: IntegrationKey): FlatListListItem => ({ + title: key.name, + subText: , + secondaryAction: ( + setDeleteDialog(key.id)} + size='large' + > + + + ), + }), + ) + return ( @@ -151,6 +186,22 @@ export default function IntegrationKeyList(props: { ) } /> + {!!extItems.length && ( + + + + }> + Externally Managed Keys + + + + + + + )} diff --git a/web/src/app/worker/worker.ts b/web/src/app/worker/worker.ts index 15692936fd..bb5a4c4871 100644 --- a/web/src/app/worker/worker.ts +++ b/web/src/app/worker/worker.ts @@ -1,7 +1,19 @@ import methods from './methods' self.onmessage = (e) => { - const method = e.data.method as keyof typeof methods - const result = methods[method](e.data.arg) + const methodName = e.data.method as keyof typeof methods + + if (!(methodName in methods)) { + // Shouldn't happen, but ensures that we can't unknowingly + // call a method that doesn't exist, or is on the prototype. + throw new Error('Invalid method') + } + + const method = methods[methodName] + if (typeof method !== 'function') { + throw new Error('Method is not a function') + } + + const result = method(e.data.arg) self.postMessage(result) } diff --git a/web/src/schema.d.ts b/web/src/schema.d.ts index cf76ef5f1d..44b6bd664f 100644 --- a/web/src/schema.d.ts +++ b/web/src/schema.d.ts @@ -220,6 +220,7 @@ export interface CreateHeartbeatMonitorInput { } export interface CreateIntegrationKeyInput { + externalSystemName?: null | string name: string serviceID?: null | string type: IntegrationKeyType @@ -534,6 +535,7 @@ export type InlineDisplayInfo = export type Int = string export interface IntegrationKey { + externalSystemName?: null | string href: string id: string name: string diff --git a/yarn.lock b/yarn.lock index 173b23a5d1..5bcca3bf88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1500,7 +1500,16 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.1.2": + version: 7.24.4 + resolution: "@babel/runtime@npm:7.24.4" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: 2f27d4c0ffac7ae7999ac0385e1106f2a06992a8bdcbf3da06adcac7413863cd08c198c2e4e970041bbea849e17f02e1df18875539b6afba76c781b6b59a07c3 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.23.9 resolution: "@babel/runtime@npm:7.23.9" dependencies: @@ -2586,7 +2595,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.0, @eslint-community/regexpp@npm:^4.6.1": +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.6.0, @eslint-community/regexpp@npm:^4.6.1": version: 4.10.0 resolution: "@eslint-community/regexpp@npm:4.10.0" checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b @@ -2643,7 +2652,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.0.5, @floating-ui/react-dom@npm:^2.0.6": +"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.0.6, @floating-ui/react-dom@npm:^2.0.8": version: 2.0.8 resolution: "@floating-ui/react-dom@npm:2.0.8" dependencies: @@ -3262,14 +3271,14 @@ __metadata: languageName: node linkType: hard -"@mui/base@npm:5.0.0-beta.32": - version: 5.0.0-beta.32 - resolution: "@mui/base@npm:5.0.0-beta.32" +"@mui/base@npm:5.0.0-beta.33": + version: 5.0.0-beta.33 + resolution: "@mui/base@npm:5.0.0-beta.33" dependencies: "@babel/runtime": ^7.23.8 - "@floating-ui/react-dom": ^2.0.5 + "@floating-ui/react-dom": ^2.0.6 "@mui/types": ^7.2.13 - "@mui/utils": ^5.15.5 + "@mui/utils": ^5.15.6 "@popperjs/core": ^2.11.8 clsx: ^2.1.0 prop-types: ^15.8.1 @@ -3280,18 +3289,18 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 5f27be8914c072ffcbe6720de9aa6129180e68927657e8bcbc03a6f322d1ee6c6740a199d72ed0b490a7b29b79cc0c59d1e05a427089b17f4cbc9cc756e67506 + checksum: 5724b2ad6971254944cada34ed06e7bda90c719d01ff5491af71f58e9a1cb6d804dda03bfc16fe5220f817acea18b178ef6b16475e99551ab89348e1e3057bd2 languageName: node linkType: hard -"@mui/base@npm:5.0.0-beta.33": - version: 5.0.0-beta.33 - resolution: "@mui/base@npm:5.0.0-beta.33" +"@mui/base@npm:5.0.0-beta.40": + version: 5.0.0-beta.40 + resolution: "@mui/base@npm:5.0.0-beta.40" dependencies: - "@babel/runtime": ^7.23.8 - "@floating-ui/react-dom": ^2.0.6 - "@mui/types": ^7.2.13 - "@mui/utils": ^5.15.6 + "@babel/runtime": ^7.23.9 + "@floating-ui/react-dom": ^2.0.8 + "@mui/types": ^7.2.14 + "@mui/utils": ^5.15.14 "@popperjs/core": ^2.11.8 clsx: ^2.1.0 prop-types: ^15.8.1 @@ -3302,14 +3311,14 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 5724b2ad6971254944cada34ed06e7bda90c719d01ff5491af71f58e9a1cb6d804dda03bfc16fe5220f817acea18b178ef6b16475e99551ab89348e1e3057bd2 + checksum: 9c084ee67de372411a71af5eca9a5367db9f5bce57bb43973629c522760fe64fa2a43d2934dccd24d6dcbcd0ed399c5fc5c461226c86104f5767de1c9b8deba2 languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^5.15.5": - version: 5.15.7 - resolution: "@mui/core-downloads-tracker@npm:5.15.7" - checksum: cdaea04222020086fd68e25bdf0f4dfdfc9a3b58a558297ef0a247f02cce8ea7671f9a31c07c5b53cfe553d24110baed2b03b701b1bea60f5c2b2e3ba56ba6fc +"@mui/core-downloads-tracker@npm:^5.15.15": + version: 5.15.15 + resolution: "@mui/core-downloads-tracker@npm:5.15.15" + checksum: 3e99a04e03f66d5fa5f0c23cdce0f9fa2331ba08c99a75dc2347ccaa1c6ed520153e04aaeb0d613c9dca099a3e6242558a6284c33d93f95cc65e3243b17860bc languageName: node linkType: hard @@ -3358,19 +3367,19 @@ __metadata: languageName: node linkType: hard -"@mui/material@npm:5.15.5": - version: 5.15.5 - resolution: "@mui/material@npm:5.15.5" +"@mui/material@npm:5.15.15": + version: 5.15.15 + resolution: "@mui/material@npm:5.15.15" dependencies: - "@babel/runtime": ^7.23.8 - "@mui/base": 5.0.0-beta.32 - "@mui/core-downloads-tracker": ^5.15.5 - "@mui/system": ^5.15.5 - "@mui/types": ^7.2.13 - "@mui/utils": ^5.15.5 + "@babel/runtime": ^7.23.9 + "@mui/base": 5.0.0-beta.40 + "@mui/core-downloads-tracker": ^5.15.15 + "@mui/system": ^5.15.15 + "@mui/types": ^7.2.14 + "@mui/utils": ^5.15.14 "@types/react-transition-group": ^4.4.10 clsx: ^2.1.0 - csstype: ^3.1.2 + csstype: ^3.1.3 prop-types: ^15.8.1 react-is: ^18.2.0 react-transition-group: ^4.4.5 @@ -3387,11 +3396,28 @@ __metadata: optional: true "@types/react": optional: true - checksum: dbfcb31810c674d9ab3b9145752433de3917d9c0d1b491bdff84c44b8f1124e8fe8ab04fa09b974b497983b7bd3011b86fb441ad365f979f971d3ddb46712060 + checksum: ee0dc22fc4d617f7cf69f2451b6d5139978e6c5319e3056e7719159aff786ee3b80abd07691e230371811d9b5b574aef4559d7855bfe2f8493d596d960a91ab7 languageName: node linkType: hard -"@mui/private-theming@npm:^5.15.5, @mui/private-theming@npm:^5.15.6, @mui/private-theming@npm:^5.15.7": +"@mui/private-theming@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/private-theming@npm:5.15.14" + dependencies: + "@babel/runtime": ^7.23.9 + "@mui/utils": ^5.15.14 + prop-types: ^15.8.1 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 1b1ef54e8281c9b13fcc58f4c39682efc610946a68402283c19fcfbce8a7d7a231d61b536d6df9bf7a59a1426591bd403a453a59eb8efb9689437fb58554dc8c + languageName: node + linkType: hard + +"@mui/private-theming@npm:^5.15.6": version: 5.15.7 resolution: "@mui/private-theming@npm:5.15.7" dependencies: @@ -3408,13 +3434,13 @@ __metadata: languageName: node linkType: hard -"@mui/styled-engine@npm:^5.15.6, @mui/styled-engine@npm:^5.15.7": - version: 5.15.7 - resolution: "@mui/styled-engine@npm:5.15.7" +"@mui/styled-engine@npm:^5.15.14, @mui/styled-engine@npm:^5.15.6": + version: 5.15.14 + resolution: "@mui/styled-engine@npm:5.15.14" dependencies: "@babel/runtime": ^7.23.9 "@emotion/cache": ^11.11.0 - csstype: ^3.1.2 + csstype: ^3.1.3 prop-types: ^15.8.1 peerDependencies: "@emotion/react": ^11.4.1 @@ -3425,21 +3451,21 @@ __metadata: optional: true "@emotion/styled": optional: true - checksum: 270901d08bf662bf652d3cb18684ea9c90658b1fec7a8bc3300e87414c70acbe8defe79745bda85ac6fd015bf9d77ce7878386944de9b3ad087072ac7161343b + checksum: 23b45c859a4f0d2b10933d06a6082c0ff093f7b6d8d32a2bfe3a6e515fe46d7a38ca9e7150d45c025a2e98d963bae9a5991d131cf4748b62670075ef0fa321ed languageName: node linkType: hard -"@mui/styles@npm:5.15.5": - version: 5.15.5 - resolution: "@mui/styles@npm:5.15.5" +"@mui/styles@npm:5.15.15": + version: 5.15.15 + resolution: "@mui/styles@npm:5.15.15" dependencies: - "@babel/runtime": ^7.23.8 + "@babel/runtime": ^7.23.9 "@emotion/hash": ^0.9.1 - "@mui/private-theming": ^5.15.5 - "@mui/types": ^7.2.13 - "@mui/utils": ^5.15.5 + "@mui/private-theming": ^5.15.14 + "@mui/types": ^7.2.14 + "@mui/utils": ^5.15.14 clsx: ^2.1.0 - csstype: ^3.1.2 + csstype: ^3.1.3 hoist-non-react-statics: ^3.3.2 jss: ^10.10.0 jss-plugin-camel-case: ^10.10.0 @@ -3456,7 +3482,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: dc131c0e7763f7ac4fd96a1db3dd85abf59de5429a73f7cf9ed15d423fb9bb5883835c1951d36e8804da9ab2ed13d603ed349f12e7cd15885f8279d90d13511b + checksum: 5e436a8a22ae9a2e6fcb63ead3a6eff2092cc9731fce0b22d04ab9c67639a173a0e27fb617628e7720592862118a32248be458bedfabaf1de5448543a97b0307 languageName: node linkType: hard @@ -3488,17 +3514,17 @@ __metadata: languageName: node linkType: hard -"@mui/system@npm:^5.15.5, @mui/system@npm:^5.15.6": - version: 5.15.7 - resolution: "@mui/system@npm:5.15.7" +"@mui/system@npm:^5.15.15, @mui/system@npm:^5.15.6": + version: 5.15.15 + resolution: "@mui/system@npm:5.15.15" dependencies: "@babel/runtime": ^7.23.9 - "@mui/private-theming": ^5.15.7 - "@mui/styled-engine": ^5.15.7 - "@mui/types": ^7.2.13 - "@mui/utils": ^5.15.7 + "@mui/private-theming": ^5.15.14 + "@mui/styled-engine": ^5.15.14 + "@mui/types": ^7.2.14 + "@mui/utils": ^5.15.14 clsx: ^2.1.0 - csstype: ^3.1.2 + csstype: ^3.1.3 prop-types: ^15.8.1 peerDependencies: "@emotion/react": ^11.5.0 @@ -3512,23 +3538,23 @@ __metadata: optional: true "@types/react": optional: true - checksum: 346ae540b511b3d5baee544b8a8304fd9bbf87c076faa1abec973f4e9a3c412ec8d8762138902229f91112c2826924c67e8b24b5273a4f6476e7c5b361e581b5 + checksum: 9ca96d5f66b2a9d6471909cc98c671eea5ec0a6d58a7ec071073b9e5200b95c3f017f0ca5cc946abc7f83074bd11830ca18f5e30bc98e25cd6ca217bd1b3a26f languageName: node linkType: hard -"@mui/types@npm:^7.2.13": - version: 7.2.13 - resolution: "@mui/types@npm:7.2.13" +"@mui/types@npm:^7.2.13, @mui/types@npm:^7.2.14": + version: 7.2.14 + resolution: "@mui/types@npm:7.2.14" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 58dfc96f9654288519ff01d6b54e6a242f05cadad51210deb85710a81be4fa1501a116c8968e2614b16c748fc1f407dc23beeeeae70fa37fceb6c6de876ff70d + checksum: 615c9f9110933157f5d3c4fee69d6e70b98fc0d9ebc3b63079b6a1e23e6b389748687a25ab4ac15b56166fc228885da87c3929503b41fa322cfdee0f6d411206 languageName: node linkType: hard -"@mui/utils@npm:^5.14.16, @mui/utils@npm:^5.15.5, @mui/utils@npm:^5.15.6, @mui/utils@npm:^5.15.7": +"@mui/utils@npm:^5.14.16, @mui/utils@npm:^5.15.6, @mui/utils@npm:^5.15.7": version: 5.15.7 resolution: "@mui/utils@npm:5.15.7" dependencies: @@ -3546,6 +3572,24 @@ __metadata: languageName: node linkType: hard +"@mui/utils@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/utils@npm:5.15.14" + dependencies: + "@babel/runtime": ^7.23.9 + "@types/prop-types": ^15.7.11 + prop-types: ^15.8.1 + react-is: ^18.2.0 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 36543ba7e3b65fb3219ed27e8f1455aff15b47a74c9b642c63e60774e22baa6492a196079e72bcfa5a570421dab32160398f892110bd444428bcf8b266b11893 + languageName: node + linkType: hard + "@mui/x-data-grid@npm:6.19.6": version: 6.19.6 resolution: "@mui/x-data-grid@npm:6.19.6" @@ -6280,10 +6324,10 @@ __metadata: languageName: node linkType: hard -"@types/chance@npm:1.1.4": - version: 1.1.4 - resolution: "@types/chance@npm:1.1.4" - checksum: 0ce6a654e6702237800b01ae7889209a98752ed4792d810b5007c27c046972988ac6fcc36063bdc7ede37d3ab1d0fd0a7bb3e405d4566c80debd9e663a90bbb0 +"@types/chance@npm:1.1.6": + version: 1.1.6 + resolution: "@types/chance@npm:1.1.6" + checksum: a1845ff0a70989d581b8f7628aaef1807276c0ae22d0c038013ebcac4f4bd57e6a765319377994cc3cae2122838638fdb2afe5232ee6b987b3f7ea2cddecbead languageName: node linkType: hard @@ -6615,7 +6659,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 @@ -6858,13 +6902,20 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.4, @types/semver@npm:^7.5.0": +"@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.4": version: 7.5.6 resolution: "@types/semver@npm:7.5.6" checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 languageName: node linkType: hard +"@types/semver@npm:^7.5.8": + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: ea6f5276f5b84c55921785a3a27a3cd37afee0111dfe2bcb3e03c31819c197c782598f17f0b150a69d453c9584cd14c4c4d7b9a55d2c5e6cacd4d66fdb3b3663 + languageName: node + linkType: hard + "@types/send@npm:*": version: 0.17.4 resolution: "@types/send@npm:0.17.4" @@ -7010,28 +7061,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.1.0" +"@typescript-eslint/eslint-plugin@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/eslint-plugin@npm:7.7.0" dependencies: - "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 7.1.0 - "@typescript-eslint/type-utils": 7.1.0 - "@typescript-eslint/utils": 7.1.0 - "@typescript-eslint/visitor-keys": 7.1.0 + "@eslint-community/regexpp": ^4.10.0 + "@typescript-eslint/scope-manager": 7.7.0 + "@typescript-eslint/type-utils": 7.7.0 + "@typescript-eslint/utils": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 debug: ^4.3.4 graphemer: ^1.4.0 - ignore: ^5.2.4 + ignore: ^5.3.1 natural-compare: ^1.4.0 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 + semver: ^7.6.0 + ts-api-utils: ^1.3.0 peerDependencies: "@typescript-eslint/parser": ^7.0.0 eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 01d56d92560980fa8daaef2cb5b1e9b5231a766d6aa02697a87d079575399c90f3864e5d6032f889672329cece885faecf696683e380ce23a094fc6ef409572d + checksum: f97348425d114282407f4f524cdc618199d0d6d86e4e556709063b07611192068872cbd7f612cbd670617d958ee4519b25eeca0bccbac1b08433ce41511d3825 languageName: node linkType: hard @@ -7073,30 +7124,30 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/scope-manager@npm:7.1.0" +"@typescript-eslint/scope-manager@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/scope-manager@npm:7.7.0" dependencies: - "@typescript-eslint/types": 7.1.0 - "@typescript-eslint/visitor-keys": 7.1.0 - checksum: 737c010cb60eedb2824038995150146a2099b09d0194ee0e7a2b730f29603775eba54b5260731a26e1056c4cdcc1847b5ea505228e9c240b6e31e3ed4b7a1d75 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 + checksum: cb280d4aa64cdefee362ef97b6fde3ae86a376fccff7f012e4e635ffe544dd90be37b340c7099784d0fbebb37b925aab6b53195825b41cee38e2382d0b552871 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/type-utils@npm:7.1.0" +"@typescript-eslint/type-utils@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/type-utils@npm:7.7.0" dependencies: - "@typescript-eslint/typescript-estree": 7.1.0 - "@typescript-eslint/utils": 7.1.0 + "@typescript-eslint/typescript-estree": 7.7.0 + "@typescript-eslint/utils": 7.7.0 debug: ^4.3.4 - ts-api-utils: ^1.0.1 + ts-api-utils: ^1.3.0 peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 07c4261da12ac57a7f03064192e20bdc473074839057deb7a2d289ceb5f205f419fb5c753d81a2ed13493ae3cfe60d371348489a326474d9c4cb810c3dd96523 + checksum: 74c07e4fcc8e6ee7870a161596d25ecfa22624947d94ca9af7147590caa13b6388f0e55101961ab02f77e7e6cffdaf19895575d7329dda50fa18fc71bf15f6b7 languageName: node linkType: hard @@ -7114,10 +7165,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/types@npm:7.1.0" - checksum: ad1e95ee83e9af7569c61260e62e4f4a42c8b82c57c33880c24dba44d1ab6792f5063e71ddf5176a1846b97158caba456805271787785250a937bba0e3df06d0 +"@typescript-eslint/types@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/types@npm:7.7.0" + checksum: c47aae2c1474b85fab012e0518c57685c595f11775b615b6a6749f943aa7a98554d9eb7054114850679f46699578049998408a492e0c1abd3bded2aee8e261a5 languageName: node linkType: hard @@ -7158,39 +7209,39 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.1.0" +"@typescript-eslint/typescript-estree@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.7.0" dependencies: - "@typescript-eslint/types": 7.1.0 - "@typescript-eslint/visitor-keys": 7.1.0 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/visitor-keys": 7.7.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 - minimatch: 9.0.3 - semver: ^7.5.4 - ts-api-utils: ^1.0.1 + minimatch: ^9.0.4 + semver: ^7.6.0 + ts-api-utils: ^1.3.0 peerDependenciesMeta: typescript: optional: true - checksum: a4db9f2b5094f3fdeaa09ca93ffefe23a7cfab3924c870b7277d36d1f9e3e9e0bd4fb10d9a4bae75d4ce5c0d1a0193888742f080e7f43a9f1b6d105f05f570c0 + checksum: 54d16b2a083bff3c6d38fbee56465403bbcba411bf25e94f2d8bbbbd8b4b35c151c7845997e5141224f8dba5bc1f34964762713035d49113700efd7381246d02 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/utils@npm:7.1.0" +"@typescript-eslint/utils@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/utils@npm:7.7.0" dependencies: "@eslint-community/eslint-utils": ^4.4.0 - "@types/json-schema": ^7.0.12 - "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 7.1.0 - "@typescript-eslint/types": 7.1.0 - "@typescript-eslint/typescript-estree": 7.1.0 - semver: ^7.5.4 + "@types/json-schema": ^7.0.15 + "@types/semver": ^7.5.8 + "@typescript-eslint/scope-manager": 7.7.0 + "@typescript-eslint/types": 7.7.0 + "@typescript-eslint/typescript-estree": 7.7.0 + semver: ^7.6.0 peerDependencies: eslint: ^8.56.0 - checksum: 9bf1be1fe7fad71412f5150d6ab74085b50da0f495e15a26f02239c9198a84b9376a827cbaa5ac0372ea914a5731168ac2e8a33190f0bbb84114aed27761959b + checksum: 830ff3af96538083d7513c211e39f07375b7e973c135a2b9bbae1ad7509bd4dce33a144a22d896a2ff4c18e9fcccd423535b6f9bb8adafe36e800f16bc53378c languageName: node linkType: hard @@ -7232,13 +7283,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.1.0": - version: 7.1.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.1.0" +"@typescript-eslint/visitor-keys@npm:7.7.0": + version: 7.7.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.7.0" dependencies: - "@typescript-eslint/types": 7.1.0 - eslint-visitor-keys: ^3.4.1 - checksum: 7ddac02dde4e16960ca87f0c05e5c5176fef6203bbf39d217ae15f8db498c262677a5799a258960a8d6bbcbc2ffbb799841e32276d2867f1e2f88bd988606092 + "@typescript-eslint/types": 7.7.0 + eslint-visitor-keys: ^3.4.3 + checksum: 16d0b63b9d98ea1d3d20bd6f9dc3cbd2674055845ad493d98118669d54792b1c167f57ae25beaae2c1107ed07012ac3c3093cca978b2ab49833dc491bc302b33 languageName: node linkType: hard @@ -9358,13 +9409,6 @@ __metadata: languageName: node linkType: hard -"css-unit-converter@npm:^1.1.1": - version: 1.1.2 - resolution: "css-unit-converter@npm:1.1.2" - checksum: 07888033346a5128f34dbe2f72884c966d24e9f29db24416dcde92860242490617ef9a178ac193a92f730834bbeea026cdc7027701d92ba9bbbe59db7a37eb2a - languageName: node - linkType: hard - "css-vendor@npm:^2.0.8": version: 2.0.8 resolution: "css-vendor@npm:2.0.8" @@ -9391,7 +9435,7 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.0.2, csstype@npm:^3.1.2": +"csstype@npm:^3.0.2, csstype@npm:^3.1.2, csstype@npm:^3.1.3": version: 3.1.3 resolution: "csstype@npm:3.1.3" checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 @@ -13122,7 +13166,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.0": +"ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.0, ignore@npm:^5.3.1": version: 5.3.1 resolution: "ignore@npm:5.3.1" checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3 @@ -16166,6 +16210,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.4 + resolution: "minimatch@npm:9.0.4" + dependencies: + brace-expansion: ^2.0.1 + checksum: cf717f597ec3eed7dabc33153482a2e8d49f4fd3c26e58fd9c71a94c5029a0838728841b93f46bf1263b65a8010e2ee800d0dc9b004ab8ba8b6d1ec07cc115b5 + languageName: node + linkType: hard + "minimist@npm:^1.1.1, minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -17345,13 +17398,6 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^3.3.0": - version: 3.3.1 - resolution: "postcss-value-parser@npm:3.3.1" - checksum: 62cd26e1cdbcf2dcc6bcedf3d9b409c9027bc57a367ae20d31dd99da4e206f730689471fd70a2abe866332af83f54dc1fa444c589e2381bf7f8054c46209ce16 - languageName: node - linkType: hard - "postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -17979,7 +18025,7 @@ __metadata: languageName: node linkType: hard -"react-smooth@npm:^2.0.2": +"react-smooth@npm:^2.0.4": version: 2.0.5 resolution: "react-smooth@npm:2.0.5" dependencies: @@ -18152,24 +18198,24 @@ __metadata: languageName: node linkType: hard -"recharts@npm:2.8.0": - version: 2.8.0 - resolution: "recharts@npm:2.8.0" +"recharts@npm:2.9.2": + version: 2.9.2 + resolution: "recharts@npm:2.9.2" dependencies: classnames: ^2.2.5 eventemitter3: ^4.0.1 lodash: ^4.17.19 react-is: ^16.10.2 react-resize-detector: ^8.0.4 - react-smooth: ^2.0.2 + react-smooth: ^2.0.4 recharts-scale: ^0.4.4 - reduce-css-calc: ^2.1.8 + tiny-invariant: ^1.3.1 victory-vendor: ^36.6.8 peerDependencies: prop-types: ^15.6.0 react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: 4638bd5c6c2af8f5c79de5e13cce0e38f06e0bbb0a3c4df27a9b12632fd72c0a0604c8246f55e830f323dfa84a3da7cb2634c2243bb9c775d899fd71f9d4c87a + checksum: 77a87e3d91229ac5400240409568e3345ded50fc117e70e43e61a135b44bbc3164048704393aa988201ea2989278e1c4e96ce350f6a2f87044d1f0a48f290e84 languageName: node linkType: hard @@ -18183,16 +18229,6 @@ __metadata: languageName: node linkType: hard -"reduce-css-calc@npm:^2.1.8": - version: 2.1.8 - resolution: "reduce-css-calc@npm:2.1.8" - dependencies: - css-unit-converter: ^1.1.1 - postcss-value-parser: ^3.3.0 - checksum: 8fd27c06c4b443b84749a69a8b97d10e6ec7d142b625b41923a8807abb22b9e37e44df14e26cc606a802957be07bdce5e8ee2976a6952a7b438a7727007101e9 - languageName: node - linkType: hard - "redux-devtools-extension@npm:2.13.9": version: 2.13.9 resolution: "redux-devtools-extension@npm:2.13.9" @@ -18779,8 +18815,8 @@ __metadata: "@material/material-color-utilities": 0.2.7 "@mui/icons-material": 5.15.13 "@mui/lab": 5.0.0-alpha.162 - "@mui/material": 5.15.5 - "@mui/styles": 5.15.5 + "@mui/material": 5.15.15 + "@mui/styles": 5.15.15 "@mui/system": 5.15.6 "@mui/x-data-grid": 6.19.6 "@playwright/test": 1.41.2 @@ -18794,7 +18830,7 @@ __metadata: "@storybook/test": 8.0.8 "@storybook/test-runner": 0.17.0 "@storybook/types": 8.0.6 - "@types/chance": 1.1.4 + "@types/chance": 1.1.6 "@types/diff": 5.0.8 "@types/glob": 8.1.0 "@types/jest": 29.5.12 @@ -18807,7 +18843,7 @@ __metadata: "@types/react-dom": 18.2.22 "@types/react-transition-group": 4.4.10 "@types/react-virtualized-auto-sizer": 1.0.4 - "@typescript-eslint/eslint-plugin": 7.1.0 + "@typescript-eslint/eslint-plugin": 7.7.0 "@typescript-eslint/parser": 6.21.0 "@urql/exchange-retry": 1.2.1 bowser: 2.11.0 @@ -18861,7 +18897,7 @@ __metadata: react-redux: 8.1.3 react-transition-group: 4.4.5 react-virtualized-auto-sizer: 1.0.20 - recharts: 2.8.0 + recharts: 2.9.2 redux: 4.2.1 redux-devtools-extension: 2.13.9 redux-thunk: 2.4.2 @@ -19037,7 +19073,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.0": +"semver@npm:7.6.0, semver@npm:^7.6.0": version: 7.6.0 resolution: "semver@npm:7.6.0" dependencies: @@ -20373,6 +20409,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^1.3.0": + version: 1.3.0 + resolution: "ts-api-utils@npm:1.3.0" + peerDependencies: + typescript: ">=4.2.0" + checksum: c746ddabfdffbf16cb0b0db32bb287236a19e583057f8649ee7c49995bb776e1d3ef384685181c11a1a480369e022ca97512cb08c517b2d2bd82c83754c97012 + languageName: node + linkType: hard + "ts-dedent@npm:^2.0.0, ts-dedent@npm:^2.2.0": version: 2.2.0 resolution: "ts-dedent@npm:2.2.0"