Merge branch 'main' into oscark/show-transformations-when-editing-scene-dashboard
This commit is contained in:
@@ -2437,17 +2437,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper.ts:5381": [
|
||||
<<<<<<< HEAD
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
||||||| eb8dfe7933d
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"]
|
||||
=======
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
>>>>>>> 6073626683dd3e9f437a533ef55fd79baaceb982
|
||||
],
|
||||
"public/app/features/dashboard-scene/utils/test-utils.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
|
||||
+11
-11
@@ -132,7 +132,7 @@
|
||||
"@types/react": "18.2.15",
|
||||
"@types/react-beautiful-dnd": "13.1.8",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@types/react-grid-layout": "1.3.2",
|
||||
"@types/react-grid-layout": "1.3.5",
|
||||
"@types/react-highlight-words": "0.16.7",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-table": "7.7.19",
|
||||
@@ -198,15 +198,15 @@
|
||||
"jest-junit": "16.0.0",
|
||||
"jest-matcher-utils": "29.7.0",
|
||||
"lerna": "7.4.1",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"mini-css-extract-plugin": "2.7.7",
|
||||
"msw": "1.3.2",
|
||||
"mutationobserver-shim": "0.3.7",
|
||||
"ngtemplate-loader": "2.1.0",
|
||||
"node-notifier": "10.0.1",
|
||||
"postcss": "8.4.31",
|
||||
"postcss-loader": "7.3.3",
|
||||
"postcss": "8.4.33",
|
||||
"postcss-loader": "7.3.4",
|
||||
"postcss-reporter": "7.0.5",
|
||||
"postcss-scss": "4.0.6",
|
||||
"postcss-scss": "4.0.9",
|
||||
"prettier": "3.0.0",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-select-event": "5.5.1",
|
||||
@@ -350,7 +350,7 @@
|
||||
"ml-regression-polynomial": "^3.0.0",
|
||||
"ml-regression-simple-linear": "^3.0.0",
|
||||
"moment": "2.29.4",
|
||||
"moment-timezone": "0.5.43",
|
||||
"moment-timezone": "0.5.44",
|
||||
"monaco-editor": "0.34.0",
|
||||
"monaco-promql": "1.7.4",
|
||||
"mousetrap": "1.6.5",
|
||||
@@ -359,7 +359,7 @@
|
||||
"nanoid": "^5.0.4",
|
||||
"node-forge": "^1.3.1",
|
||||
"ol": "7.4.0",
|
||||
"ol-ext": "4.0.10",
|
||||
"ol-ext": "4.0.13",
|
||||
"papaparse": "5.4.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"prismjs": "1.29.0",
|
||||
@@ -369,15 +369,15 @@
|
||||
"rc-drawer": "6.5.2",
|
||||
"rc-slider": "10.3.1",
|
||||
"rc-time-picker": "3.7.3",
|
||||
"rc-tree": "5.8.0",
|
||||
"re-resizable": "6.9.9",
|
||||
"rc-tree": "5.8.2",
|
||||
"re-resizable": "6.9.11",
|
||||
"react": "18.2.0",
|
||||
"react-beautiful-dnd": "13.1.1",
|
||||
"react-diff-viewer": "^3.1.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-draggable": "4.4.5",
|
||||
"react-draggable": "4.4.6",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-grid-layout": "1.4.2",
|
||||
"react-grid-layout": "1.4.4",
|
||||
"react-highlight-words": "0.20.0",
|
||||
"react-hook-form": "^7.49.2",
|
||||
"react-i18next": "^12.0.0",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"marked": "5.1.1",
|
||||
"marked-mangle": "1.1.6",
|
||||
"moment": "2.29.4",
|
||||
"moment-timezone": "0.5.43",
|
||||
"moment-timezone": "0.5.44",
|
||||
"ol": "7.4.0",
|
||||
"papaparse": "5.4.1",
|
||||
"react-use": "17.4.0",
|
||||
|
||||
@@ -128,6 +128,9 @@ var (
|
||||
// MAccessPermissionsSummary is a metric summary for loading permissions request duration when evaluating access
|
||||
MAccessPermissionsSummary prometheus.Histogram
|
||||
|
||||
// MSearchPermissionsSummary is a metric summary for searching permissions request duration
|
||||
MAccessSearchPermissionsSummary prometheus.Histogram
|
||||
|
||||
// MAccessEvaluationsSummary is a metric summary for loading permissions request duration when evaluating access
|
||||
MAccessEvaluationsSummary prometheus.Histogram
|
||||
)
|
||||
@@ -581,6 +584,12 @@ func init() {
|
||||
Namespace: ExporterName,
|
||||
})
|
||||
|
||||
MAccessSearchPermissionsSummary = prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Name: "access_search_permissions_duration",
|
||||
Help: "Histogram for the runtime of permissions search function",
|
||||
Buckets: prometheus.ExponentialBuckets(0.001, 4, 5),
|
||||
})
|
||||
|
||||
StatsTotalLibraryPanels = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "stat_totals_library_panels",
|
||||
Help: "total amount of library panels in the database",
|
||||
@@ -698,6 +707,7 @@ func initMetricVars(reg prometheus.Registerer) {
|
||||
MRenderingQueue,
|
||||
MAccessPermissionsSummary,
|
||||
MAccessEvaluationsSummary,
|
||||
MAccessSearchPermissionsSummary,
|
||||
MAlertingActiveAlerts,
|
||||
MStatTotalDashboards,
|
||||
MStatTotalFolders,
|
||||
|
||||
@@ -242,6 +242,9 @@ func (s *Service) DeclarePluginRoles(ctx context.Context, ID, name string, regs
|
||||
// SearchUsersPermissions returns all users' permissions filtered by action prefixes
|
||||
func (s *Service) SearchUsersPermissions(ctx context.Context, user identity.Requester,
|
||||
options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
|
||||
timer := prometheus.NewTimer(metrics.MAccessSearchPermissionsSummary)
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
// Filter ram permissions
|
||||
basicPermissions := map[string][]accesscontrol.Permission{}
|
||||
for role, basicRole := range s.roles {
|
||||
|
||||
@@ -112,7 +112,7 @@ func (c *OAuth) Authenticate(ctx context.Context, r *authn.Request) (*authn.Iden
|
||||
if err != nil {
|
||||
return nil, errOAuthMissingPKCE.Errorf("no pkce cookie found: %w", err)
|
||||
}
|
||||
opts = append(opts, oauth2.SetAuthURLParam(codeVerifierParamName, pkceCookie.Value))
|
||||
opts = append(opts, oauth2.VerifierOption(pkceCookie.Value))
|
||||
}
|
||||
|
||||
clientCtx := context.WithValue(ctx, oauth2.HTTPClient, c.httpClient)
|
||||
@@ -184,16 +184,13 @@ func (c *OAuth) RedirectURL(ctx context.Context, r *authn.Request) (*authn.Redir
|
||||
|
||||
var plainPKCE string
|
||||
if c.oauthCfg.UsePKCE {
|
||||
pkce, hashedPKCE, err := genPKCECode()
|
||||
verifier, err := genPKCECodeVerifier()
|
||||
if err != nil {
|
||||
return nil, errOAuthGenPKCE.Errorf("failed to generate pkce: %w", err)
|
||||
}
|
||||
|
||||
plainPKCE = pkce
|
||||
opts = append(opts,
|
||||
oauth2.SetAuthURLParam(codeChallengeParamName, hashedPKCE),
|
||||
oauth2.SetAuthURLParam(codeChallengeMethodParamName, codeChallengeMethod),
|
||||
)
|
||||
plainPKCE = verifier
|
||||
opts = append(opts, oauth2.S256ChallengeOption(plainPKCE))
|
||||
}
|
||||
|
||||
state, hashedSate, err := genOAuthState(c.cfg.SecretKey, c.oauthCfg.ClientSecret)
|
||||
@@ -233,8 +230,8 @@ func (c *OAuth) Logout(ctx context.Context, user identity.Requester, info *login
|
||||
return &authn.Redirect{URL: redirctURL}, true
|
||||
}
|
||||
|
||||
// genPKCECode returns a random URL-friendly string and it's base64 URL encoded SHA256 digest.
|
||||
func genPKCECode() (string, string, error) {
|
||||
// genPKCECodeVerifier returns code verifier that 128 characters random URL-friendly string.
|
||||
func genPKCECodeVerifier() (string, error) {
|
||||
// IETF RFC 7636 specifies that the code verifier should be 43-128
|
||||
// characters from a set of unreserved URI characters which is
|
||||
// almost the same as the set of characters in base64url.
|
||||
@@ -249,14 +246,12 @@ func genPKCECode() (string, string, error) {
|
||||
raw := make([]byte, 96)
|
||||
_, err := rand.Read(raw)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
ascii := make([]byte, 128)
|
||||
base64.RawURLEncoding.Encode(ascii, raw)
|
||||
|
||||
shasum := sha256.Sum256(ascii)
|
||||
pkce := base64.RawURLEncoding.EncodeToString(shasum[:])
|
||||
return string(ascii), pkce, nil
|
||||
return string(ascii), nil
|
||||
}
|
||||
|
||||
func genOAuthState(secret, seed string) (string, string, error) {
|
||||
|
||||
@@ -268,7 +268,7 @@ func TestOAuth_RedirectURL(t *testing.T) {
|
||||
{
|
||||
desc: "should generate redirect url with pkce if configured",
|
||||
oauthCfg: &social.OAuthInfo{UsePKCE: true},
|
||||
numCallOptions: 2,
|
||||
numCallOptions: 1,
|
||||
authCodeUrlCalled: true,
|
||||
},
|
||||
}
|
||||
@@ -404,6 +404,12 @@ func TestOAuth_Logout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenPKCECodeVerifier(t *testing.T) {
|
||||
verifier, err := genPKCECodeVerifier()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, verifier, 128)
|
||||
}
|
||||
|
||||
type mockConnector struct {
|
||||
AuthCodeURLFunc func(state string, opts ...oauth2.AuthCodeOption) string
|
||||
social.SocialConnector
|
||||
|
||||
@@ -3,6 +3,7 @@ package cloudwatch
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -121,6 +122,7 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger
|
||||
Error: fmt.Errorf("metric request error: %q", err),
|
||||
}
|
||||
resultChan <- &responseWrapper{
|
||||
RefId: getQueryRefIdFromErrorString(err.Error(), requestQueries),
|
||||
DataResponse: &dataResponse,
|
||||
}
|
||||
}
|
||||
@@ -132,3 +134,17 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func getQueryRefIdFromErrorString(err string, queries []*models.CloudWatchQuery) string {
|
||||
// error can be in format "Error in expression 'test': Invalid syntax"
|
||||
// so we can find the query id or ref id between the quotations
|
||||
erroredRefId := ""
|
||||
|
||||
for _, query := range queries {
|
||||
if regexp.MustCompile(`'`+query.RefId+`':`).MatchString(err) || regexp.MustCompile(`'`+query.Id+`':`).MatchString(err) {
|
||||
erroredRefId = query.RefId
|
||||
}
|
||||
}
|
||||
// if errorRefId is empty, it means the error concerns all queries (error metric limit exceeded, for example)
|
||||
return erroredRefId
|
||||
}
|
||||
|
||||
+4
-12
@@ -16,12 +16,7 @@ export interface ContactPointSelectorProps {
|
||||
}
|
||||
export function ContactPointSelector({ alertManager, contactPoints, onSelectContactPoint }: ContactPointSelectorProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
formState: { errors },
|
||||
watch,
|
||||
} = useFormContext<RuleFormValues>();
|
||||
const { control, watch } = useFormContext<RuleFormValues>();
|
||||
|
||||
const options = contactPoints.map((receiver) => {
|
||||
const integrations = receiver?.grafana_managed_receiver_configs;
|
||||
@@ -39,11 +34,7 @@ export function ContactPointSelector({ alertManager, contactPoints, onSelectCont
|
||||
|
||||
return (
|
||||
<Stack direction="column">
|
||||
<Field
|
||||
label="Contact point"
|
||||
{...register(`contactPoints.${alertManager}.selectedContactPoint`, { required: true })}
|
||||
invalid={!!errors.contactPoints?.[alertManager]?.selectedContactPoint}
|
||||
>
|
||||
<Field label="Contact point">
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field }, fieldState: { error } }) => (
|
||||
<>
|
||||
@@ -65,9 +56,10 @@ export function ContactPointSelector({ alertManager, contactPoints, onSelectCont
|
||||
width={50}
|
||||
/>
|
||||
</div>
|
||||
{error && <FieldValidationMessage>{'Contact point is required.'}</FieldValidationMessage>}
|
||||
{error && <FieldValidationMessage>{error.message}</FieldValidationMessage>}
|
||||
</>
|
||||
)}
|
||||
rules={{ required: { value: true, message: 'Contact point is required.' } }}
|
||||
control={control}
|
||||
name={`contactPoints.${alertManager}.selectedContactPoint`}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { CustomVariableModel, DataQueryError, DataQueryRequest, DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { BackendDataSourceResponse, toDataQueryResponse } from '@grafana/runtime';
|
||||
import { CustomVariableModel, DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings } from '@grafana/data';
|
||||
|
||||
import { CloudWatchMetricsQueryRunner } from '../query-runner/CloudWatchMetricsQueryRunner';
|
||||
import { CloudWatchJsonData, CloudWatchQuery } from '../types';
|
||||
@@ -10,23 +9,17 @@ import { CloudWatchSettings, setupMockedTemplateService } from './CloudWatchData
|
||||
import { TimeRangeMock } from './timeRange';
|
||||
|
||||
export function setupMockedMetricsQueryRunner({
|
||||
data = {
|
||||
results: {},
|
||||
},
|
||||
response = { data: [] },
|
||||
variables,
|
||||
errorResponse,
|
||||
instanceSettings = CloudWatchSettings,
|
||||
}: {
|
||||
data?: BackendDataSourceResponse;
|
||||
response?: DataQueryResponse;
|
||||
variables?: CustomVariableModel[];
|
||||
errorResponse?: DataQueryError;
|
||||
instanceSettings?: DataSourceInstanceSettings<CloudWatchJsonData>;
|
||||
} = {}) {
|
||||
const templateService = setupMockedTemplateService(variables);
|
||||
|
||||
const queryMock = errorResponse
|
||||
? jest.fn().mockImplementation(() => throwError(errorResponse))
|
||||
: jest.fn().mockReturnValue(of(toDataQueryResponse({ data })));
|
||||
const queryMock = jest.fn().mockImplementation(() => of(response));
|
||||
const runner = new CloudWatchMetricsQueryRunner(instanceSettings, templateService);
|
||||
|
||||
const request: DataQueryRequest<CloudWatchQuery> = {
|
||||
|
||||
@@ -3,8 +3,8 @@ import React from 'react';
|
||||
|
||||
import { GrafanaTheme2, DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { ConfigSection } from '@grafana/experimental';
|
||||
import { DataSourcePicker } from '@grafana/runtime';
|
||||
import { Alert, Field, InlineField, useStyles2 } from '@grafana/ui';
|
||||
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
|
||||
+202
-144
@@ -2,7 +2,7 @@ import { of } from 'rxjs';
|
||||
|
||||
import { CustomVariableModel, getFrameDisplayName, VariableHide } from '@grafana/data';
|
||||
import { dateTime } from '@grafana/data/src/datetime/moment_wrapper';
|
||||
import { BackendDataSourceResponse } from '@grafana/runtime';
|
||||
import { toDataQueryResponse } from '@grafana/runtime';
|
||||
import * as redux from 'app/store/store';
|
||||
|
||||
import {
|
||||
@@ -17,18 +17,28 @@ import {
|
||||
import { initialVariableModelState } from '../__mocks__/CloudWatchVariables';
|
||||
import { setupMockedMetricsQueryRunner } from '../__mocks__/MetricsQueryRunner';
|
||||
import { validMetricSearchBuilderQuery, validMetricSearchCodeQuery } from '../__mocks__/queries';
|
||||
import { MetricQueryType, MetricEditorMode, CloudWatchMetricsQuery, DataQueryError } from '../types';
|
||||
import { MetricQueryType, MetricEditorMode, CloudWatchMetricsQuery } from '../types';
|
||||
|
||||
describe('CloudWatchMetricsQueryRunner', () => {
|
||||
describe('performTimeSeriesQuery', () => {
|
||||
it('should return the same length of data as result', async () => {
|
||||
const { runner, timeRange, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
const resultsFromBEQuery = {
|
||||
data: {
|
||||
results: {
|
||||
a: { refId: 'a', series: [{ target: 'cpu', datapoints: [[1, 1]] }] },
|
||||
b: { refId: 'b', series: [{ target: 'memory', datapoints: [[2, 2]] }] },
|
||||
a: {
|
||||
refId: 'a',
|
||||
series: [{ target: 'cpu', datapoints: [[1, 2]], meta: { custom: { period: 60 } } }],
|
||||
},
|
||||
b: {
|
||||
refId: 'b',
|
||||
series: [{ target: 'cpu', datapoints: [[1, 2]], meta: { custom: { period: 120 } } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const { runner, timeRange, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(resultsFromBEQuery),
|
||||
});
|
||||
|
||||
const observable = runner.performTimeSeriesQuery(
|
||||
@@ -47,7 +57,7 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
});
|
||||
|
||||
it('sets fields.config.interval based on period', async () => {
|
||||
const { runner, timeRange, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
const resultsFromBEQuery = {
|
||||
data: {
|
||||
results: {
|
||||
a: {
|
||||
@@ -60,6 +70,10 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const { runner, timeRange, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(resultsFromBEQuery),
|
||||
});
|
||||
|
||||
const observable = runner.performTimeSeriesQuery(
|
||||
@@ -98,31 +112,36 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const data: BackendDataSourceResponse = {
|
||||
results: {
|
||||
A: {
|
||||
tables: [],
|
||||
error: '',
|
||||
refId: 'A',
|
||||
series: [
|
||||
{
|
||||
target: 'CPUUtilization_Average',
|
||||
datapoints: [
|
||||
[1, 1483228800000],
|
||||
[2, 1483229100000],
|
||||
[5, 1483229700000],
|
||||
],
|
||||
tags: {
|
||||
InstanceId: 'i-12345678',
|
||||
const resultsFromBEQuery = {
|
||||
data: {
|
||||
results: {
|
||||
A: {
|
||||
tables: [],
|
||||
error: '',
|
||||
refId: 'A',
|
||||
series: [
|
||||
{
|
||||
target: 'CPUUtilization_Average',
|
||||
datapoints: [
|
||||
[1, 1483228800000],
|
||||
[2, 1483229100000],
|
||||
[5, 1483229700000],
|
||||
],
|
||||
tags: {
|
||||
InstanceId: 'i-12345678',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should generate the correct query', async () => {
|
||||
const { runner, queryMock, request } = setupMockedMetricsQueryRunner({ data });
|
||||
const { runner, queryMock, request } = setupMockedMetricsQueryRunner({
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(resultsFromBEQuery),
|
||||
});
|
||||
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith(() => {
|
||||
expect(queryMock.mock.calls[0][0].targets).toMatchObject(
|
||||
@@ -159,7 +178,8 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
];
|
||||
|
||||
const { runner, queryMock, request } = setupMockedMetricsQueryRunner({
|
||||
data,
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(resultsFromBEQuery),
|
||||
variables: [periodIntervalVariable],
|
||||
});
|
||||
|
||||
@@ -169,110 +189,111 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
});
|
||||
|
||||
it('should return series list', async () => {
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({ data });
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(resultsFromBEQuery),
|
||||
});
|
||||
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith((received) => {
|
||||
const result = received[0];
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(
|
||||
data.results.A.series?.length && data.results.A.series[0].target
|
||||
);
|
||||
expect(result.data[0].fields[1].values[0]).toBe(
|
||||
data.results.A.series?.length && data.results.A.series[0].datapoints[0][0]
|
||||
);
|
||||
expect(getFrameDisplayName(result.data[0])).toBe('CPUUtilization_Average');
|
||||
expect(result.data[0].fields[1].values[0]).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('and throttling exception is thrown', () => {
|
||||
const partialQuery: CloudWatchMetricsQuery = {
|
||||
metricQueryType: MetricQueryType.Search,
|
||||
metricEditorMode: MetricEditorMode.Builder,
|
||||
queryMode: 'Metrics',
|
||||
namespace: 'AWS/EC2',
|
||||
metricName: 'CPUUtilization',
|
||||
dimensions: {
|
||||
InstanceId: 'i-12345678',
|
||||
},
|
||||
statistic: 'Average',
|
||||
period: '300',
|
||||
expression: '',
|
||||
id: '',
|
||||
region: '',
|
||||
refId: '',
|
||||
};
|
||||
|
||||
const queries: CloudWatchMetricsQuery[] = [
|
||||
{ ...partialQuery, refId: 'A', region: 'us-east-1' },
|
||||
{ ...partialQuery, refId: 'B', region: 'us-east-2' },
|
||||
{ ...partialQuery, refId: 'C', region: 'us-east-1' },
|
||||
{ ...partialQuery, refId: 'D', region: 'us-east-2' },
|
||||
{ ...partialQuery, refId: 'E', region: 'eu-north-1' },
|
||||
];
|
||||
|
||||
const dataWithThrottlingError = {
|
||||
data: {
|
||||
message: 'Throttling: exception',
|
||||
results: {
|
||||
A: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
},
|
||||
B: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'B',
|
||||
meta: {},
|
||||
},
|
||||
C: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'C',
|
||||
meta: {},
|
||||
},
|
||||
D: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'D',
|
||||
meta: {},
|
||||
},
|
||||
E: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'E',
|
||||
meta: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
redux.setStore({
|
||||
...redux.store,
|
||||
dispatch: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('and throttling exception is thrown', () => {
|
||||
const partialQuery: CloudWatchMetricsQuery = {
|
||||
metricQueryType: MetricQueryType.Search,
|
||||
metricEditorMode: MetricEditorMode.Builder,
|
||||
queryMode: 'Metrics',
|
||||
namespace: 'AWS/EC2',
|
||||
metricName: 'CPUUtilization',
|
||||
dimensions: {
|
||||
InstanceId: 'i-12345678',
|
||||
},
|
||||
statistic: 'Average',
|
||||
period: '300',
|
||||
expression: '',
|
||||
id: '',
|
||||
region: '',
|
||||
refId: '',
|
||||
};
|
||||
|
||||
const queries: CloudWatchMetricsQuery[] = [
|
||||
{ ...partialQuery, refId: 'A', region: 'us-east-1' },
|
||||
{ ...partialQuery, refId: 'B', region: 'us-east-2' },
|
||||
{ ...partialQuery, refId: 'C', region: 'us-east-1' },
|
||||
{ ...partialQuery, refId: 'D', region: 'us-east-2' },
|
||||
{ ...partialQuery, refId: 'E', region: 'eu-north-1' },
|
||||
];
|
||||
|
||||
const backendErrorResponse: DataQueryError<CloudWatchMetricsQuery> = {
|
||||
data: {
|
||||
message: 'Throttling: exception',
|
||||
results: {
|
||||
A: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
},
|
||||
B: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'B',
|
||||
meta: {},
|
||||
},
|
||||
C: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'C',
|
||||
meta: {},
|
||||
},
|
||||
D: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'D',
|
||||
meta: {},
|
||||
},
|
||||
E: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'E',
|
||||
meta: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
redux.setStore({
|
||||
...redux.store,
|
||||
dispatch: jest.fn(),
|
||||
});
|
||||
it('should display one alert error message per region+datasource combination', async () => {
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
response: toDataQueryResponse(dataWithThrottlingError),
|
||||
});
|
||||
const memoizedDebounceSpy = jest.spyOn(runner, 'debouncedThrottlingAlert');
|
||||
|
||||
it('should display one alert error message per region+datasource combination', async () => {
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({ errorResponse: backendErrorResponse });
|
||||
const memoizedDebounceSpy = jest.spyOn(runner, 'debouncedAlert');
|
||||
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith(() => {
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('CloudWatch Test Datasource', 'us-east-1');
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('CloudWatch Test Datasource', 'us-east-2');
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('CloudWatch Test Datasource', 'eu-north-1');
|
||||
expect(memoizedDebounceSpy).toBeCalledTimes(3);
|
||||
});
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith((received) => {
|
||||
expect(received[0].errors).toHaveLength(5);
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('CloudWatch Test Datasource', 'us-east-1');
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('CloudWatch Test Datasource', 'us-east-2');
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('CloudWatch Test Datasource', 'eu-north-1');
|
||||
expect(memoizedDebounceSpy).toBeCalledTimes(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -298,43 +319,80 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const data: BackendDataSourceResponse = {
|
||||
results: {
|
||||
A: {
|
||||
tables: [],
|
||||
error: '',
|
||||
refId: 'A',
|
||||
series: [
|
||||
{
|
||||
target: 'TargetResponseTime_p90.00',
|
||||
datapoints: [
|
||||
[1, 1483228800000],
|
||||
[2, 1483229100000],
|
||||
[5, 1483229700000],
|
||||
],
|
||||
tags: {
|
||||
LoadBalancer: 'lb',
|
||||
TargetGroup: 'tg',
|
||||
const responseFromBEQuery = {
|
||||
data: {
|
||||
results: {
|
||||
A: {
|
||||
tables: [],
|
||||
error: '',
|
||||
refId: 'A',
|
||||
series: [
|
||||
{
|
||||
target: 'TargetResponseTime_p90.00',
|
||||
datapoints: [
|
||||
[1, 1483228800000],
|
||||
[2, 1483229100000],
|
||||
[5, 1483229700000],
|
||||
],
|
||||
tags: {
|
||||
LoadBalancer: 'lb',
|
||||
TargetGroup: 'tg',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should return series list', async () => {
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({ data });
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(responseFromBEQuery),
|
||||
});
|
||||
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith((received) => {
|
||||
const result = received[0];
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(
|
||||
data.results.A.series?.length && data.results.A.series[0].target
|
||||
responseFromBEQuery.data.results.A.series?.length && responseFromBEQuery.data.results.A.series[0].target
|
||||
);
|
||||
expect(result.data[0].fields[1].values[0]).toBe(
|
||||
data.results.A.series?.length && data.results.A.series[0].datapoints[0][0]
|
||||
responseFromBEQuery.data.results.A.series?.length &&
|
||||
responseFromBEQuery.data.results.A.series[0].datapoints[0][0]
|
||||
);
|
||||
});
|
||||
});
|
||||
it('should pass the error list from DatasourceWithBackend srv', async () => {
|
||||
const dataWithError = {
|
||||
data: {
|
||||
results: {
|
||||
A: {
|
||||
error:
|
||||
"metric request error: \"ValidationError: Error in expression 'query': Invalid syntax\\n\\tstatus code: 400",
|
||||
status: 500,
|
||||
},
|
||||
},
|
||||
},
|
||||
status: 500,
|
||||
statusText: 'Internal Server Error',
|
||||
};
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
// DataSourceWithBackend runs toDataQueryResponse({response from CW backend})
|
||||
response: toDataQueryResponse(dataWithError),
|
||||
});
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith((received) => {
|
||||
const result = received[0];
|
||||
expect(result.data).toEqual([]);
|
||||
expect(result.errors).toEqual([
|
||||
{
|
||||
message:
|
||||
"metric request error: \"ValidationError: Error in expression 'query': Invalid syntax\\n\\tstatus code: 400",
|
||||
status: 500,
|
||||
refId: 'A',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('template variable interpolation', () => {
|
||||
|
||||
+40
-42
@@ -1,9 +1,10 @@
|
||||
import { findLast, isEmpty } from 'lodash';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React from 'react';
|
||||
import { catchError, map, Observable, of, throwError } from 'rxjs';
|
||||
import { catchError, map, Observable, of } from 'rxjs';
|
||||
|
||||
import {
|
||||
DataFrame,
|
||||
DataQueryError,
|
||||
DataQueryRequest,
|
||||
DataQueryResponse,
|
||||
DataSourceInstanceSettings,
|
||||
@@ -21,7 +22,7 @@ import { AppNotificationTimeout } from 'app/types';
|
||||
import { ThrottlingErrorMessage } from '../components/Errors/ThrottlingErrorMessage';
|
||||
import memoizedDebounce from '../memoizedDebounce';
|
||||
import { migrateMetricQuery } from '../migrations/metricQueryMigrations';
|
||||
import { CloudWatchJsonData, CloudWatchMetricsQuery, CloudWatchQuery, DataQueryError } from '../types';
|
||||
import { CloudWatchJsonData, CloudWatchMetricsQuery, CloudWatchQuery } from '../types';
|
||||
import { filterMetricsQuery } from '../utils/utils';
|
||||
|
||||
import { CloudWatchRequest } from './CloudWatchRequest';
|
||||
@@ -37,9 +38,10 @@ const displayAlert = (datasourceName: string, region: string) =>
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// This class handles execution of CloudWatch metrics query data queries
|
||||
export class CloudWatchMetricsQueryRunner extends CloudWatchRequest {
|
||||
debouncedAlert: (datasourceName: string, region: string) => void = memoizedDebounce(
|
||||
debouncedThrottlingAlert: (datasourceName: string, region: string) => void = memoizedDebounce(
|
||||
displayAlert,
|
||||
AppNotificationTimeout.Error
|
||||
);
|
||||
@@ -109,12 +111,7 @@ export class CloudWatchMetricsQueryRunner extends CloudWatchRequest {
|
||||
): Observable<DataQueryResponse> {
|
||||
return queryFn(request).pipe(
|
||||
map((res) => {
|
||||
const dataframes: DataFrame[] = res.data;
|
||||
if (!dataframes || dataframes.length <= 0) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
const lastError = findLast(res.data, (v) => !!v.error);
|
||||
const dataframes: DataFrame[] = res.data || [];
|
||||
|
||||
dataframes.forEach((frame) => {
|
||||
frame.fields.forEach((field) => {
|
||||
@@ -125,47 +122,48 @@ export class CloudWatchMetricsQueryRunner extends CloudWatchRequest {
|
||||
});
|
||||
});
|
||||
|
||||
if (res.errors?.length) {
|
||||
this.alertOnErrors(res.errors, request);
|
||||
}
|
||||
|
||||
return {
|
||||
data: dataframes,
|
||||
error: lastError ? { message: lastError.error } : undefined,
|
||||
// DataSourceWithBackend will not throw an error, instead it will return "errors" field along with the response
|
||||
errors: res.errors,
|
||||
};
|
||||
}),
|
||||
catchError((err: DataQueryError<CloudWatchMetricsQuery>) => {
|
||||
const isFrameError = err.data?.results;
|
||||
|
||||
// Error is not frame specific
|
||||
if (!isFrameError && err.data && err.data.message === 'Metric request error' && err.data.error) {
|
||||
err.message = err.data.error;
|
||||
return throwError(() => err);
|
||||
catchError((err: unknown) => {
|
||||
if (Array.isArray(err)) {
|
||||
return of({ data: [], errors: err });
|
||||
} else {
|
||||
return of({ data: [], errors: [{ message: err }] });
|
||||
}
|
||||
|
||||
// The error is either for a specific frame or for all the frames
|
||||
const results: Array<{ error?: string }> = Object.values(err.data?.results ?? {});
|
||||
const firstErrorResult = results.find((r) => r.error);
|
||||
if (firstErrorResult) {
|
||||
err.message = firstErrorResult.error;
|
||||
}
|
||||
|
||||
if (results.some((r) => r.error && /^Throttling:.*/.test(r.error))) {
|
||||
const failedRedIds = Object.keys(err.data?.results ?? {});
|
||||
const regionsAffected = Object.values(request.targets).reduce(
|
||||
(res: string[], { refId, region }) =>
|
||||
(refId && !failedRedIds.includes(refId)) || res.includes(region) ? res : [...res, region],
|
||||
[]
|
||||
);
|
||||
regionsAffected.forEach((region) => {
|
||||
const actualRegion = this.getActualRegion(region);
|
||||
if (actualRegion) {
|
||||
this.debouncedAlert(this.instanceSettings.name, actualRegion);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return throwError(() => err);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
alertOnErrors(errors: DataQueryError[], request: DataQueryRequest<CloudWatchQuery>) {
|
||||
const hasThrottlingError = errors.some(
|
||||
(err) => err.message && (/^Throttling:.*/.test(err.message) || /^Rate exceeded.*/.test(err.message))
|
||||
);
|
||||
if (hasThrottlingError) {
|
||||
const failedRefIds = errors.map((error) => error.refId).filter((refId) => refId);
|
||||
if (failedRefIds.length > 0) {
|
||||
const regionsAffected = Object.values(request.targets).reduce(
|
||||
(res: string[], { refId, region }) =>
|
||||
(refId && !failedRefIds.includes(refId)) || res.includes(region) ? res : [...res, region],
|
||||
[]
|
||||
);
|
||||
regionsAffected.forEach((region) => {
|
||||
const actualRegion = this.getActualRegion(region);
|
||||
if (actualRegion) {
|
||||
this.debouncedThrottlingAlert(this.instanceSettings.name, actualRegion);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterMetricQuery(query: CloudWatchMetricsQuery): boolean {
|
||||
return filterMetricsQuery(query);
|
||||
}
|
||||
|
||||
@@ -5,23 +5,15 @@ import React, { useCallback, useMemo } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { DataQuery, GrafanaTheme2, SelectableValue, DataTopic, QueryEditorProps } from '@grafana/data';
|
||||
import {
|
||||
Card,
|
||||
Field,
|
||||
Select,
|
||||
useStyles2,
|
||||
VerticalGroup,
|
||||
HorizontalGroup,
|
||||
Spinner,
|
||||
Switch,
|
||||
RadioButtonGroup,
|
||||
} from '@grafana/ui';
|
||||
import { Field, Select, useStyles2, Spinner, RadioButtonGroup, Stack, InlineSwitch } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { filterPanelDataToQuery } from 'app/features/query/components/QueryEditorRow';
|
||||
|
||||
import { OperationsEditorRow } from '../prometheus/querybuilder/shared/OperationsEditorRow';
|
||||
|
||||
import { DashboardDatasource } from './datasource';
|
||||
import { DashboardQuery, ResultInfo, SHARED_DASHBOARD_QUERY } from './types';
|
||||
|
||||
@@ -150,64 +142,61 @@ export function DashboardQueryEditor({ data, query, onChange, onRunQuery }: Prop
|
||||
const selected = panels.find((panel) => panel.value === query.panelId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field label="Source" description="Use the same results as panel">
|
||||
<Select
|
||||
inputId={selectId}
|
||||
placeholder="Choose panel"
|
||||
isSearchable={true}
|
||||
options={panels}
|
||||
value={selected}
|
||||
onChange={(item) => onPanelChanged(item.value!)}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<HorizontalGroup height="auto" wrap={true} align="flex-start">
|
||||
<Field
|
||||
label="Data Source"
|
||||
description="Use data or annotations from the panel"
|
||||
className={styles.horizontalField}
|
||||
>
|
||||
<RadioButtonGroup options={topics} value={query.topic === DataTopic.Annotations} onChange={onTopicChanged} />
|
||||
</Field>
|
||||
|
||||
{showTransforms && (
|
||||
<Field label="Transform" description="Apply panel transformations from the source panel">
|
||||
<Switch value={Boolean(query.withTransforms)} onChange={onTransformToggle} />
|
||||
<OperationsEditorRow>
|
||||
<Stack direction="column">
|
||||
<Stack gap={3}>
|
||||
<Field label="Source panel" description="Use query results from another panel">
|
||||
<Select
|
||||
inputId={selectId}
|
||||
placeholder="Choose panel"
|
||||
isSearchable={true}
|
||||
options={panels}
|
||||
value={selected}
|
||||
onChange={(item) => onPanelChanged(item.value!)}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
|
||||
{loadingResults ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
{results && Boolean(results.length) && (
|
||||
<Field label="Queries from panel">
|
||||
<VerticalGroup spacing="sm">
|
||||
{results.map((target, i) => (
|
||||
<Card key={`DashboardQueryRow-${i}`}>
|
||||
<Card.Heading>{target.refId}</Card.Heading>
|
||||
<Card.Figure>
|
||||
<img src={target.img} alt={target.name} title={target.name} width={40} />
|
||||
</Card.Figure>
|
||||
<Card.Meta>{target.query}</Card.Meta>
|
||||
</Card>
|
||||
))}
|
||||
</VerticalGroup>
|
||||
<Field label="Data" description="Use data or annotations from the panel">
|
||||
<RadioButtonGroup
|
||||
options={topics}
|
||||
value={query.topic === DataTopic.Annotations}
|
||||
onChange={onTopicChanged}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
{showTransforms && (
|
||||
<Field label="Transform" description="Apply transformations from the source panel">
|
||||
<InlineSwitch value={Boolean(query.withTransforms)} onChange={onTransformToggle} />
|
||||
</Field>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</Stack>
|
||||
|
||||
{loadingResults ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
{results && Boolean(results.length) && (
|
||||
<Field label="Queries from panel">
|
||||
<Stack direction="column">
|
||||
{results.map((target, i) => (
|
||||
<Stack key={i} alignItems="center" gap={1}>
|
||||
<div>{target.refId}</div>
|
||||
<img src={target.img} alt={target.name} title={target.name} width={16} />
|
||||
<div>{target.query}</div>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</Field>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</OperationsEditorRow>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
horizontalField: css({
|
||||
marginRight: theme.spacing(2),
|
||||
}),
|
||||
noQueriesText: css({
|
||||
padding: theme.spacing(1.25),
|
||||
}),
|
||||
|
||||
@@ -3110,7 +3110,7 @@ __metadata:
|
||||
marked: "npm:5.1.1"
|
||||
marked-mangle: "npm:1.1.6"
|
||||
moment: "npm:2.29.4"
|
||||
moment-timezone: "npm:0.5.43"
|
||||
moment-timezone: "npm:0.5.44"
|
||||
ol: "npm:7.4.0"
|
||||
papaparse: "npm:5.4.1"
|
||||
react: "npm:18.2.0"
|
||||
@@ -9024,12 +9024,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-grid-layout@npm:1.3.2":
|
||||
version: 1.3.2
|
||||
resolution: "@types/react-grid-layout@npm:1.3.2"
|
||||
"@types/react-grid-layout@npm:1.3.5":
|
||||
version: 1.3.5
|
||||
resolution: "@types/react-grid-layout@npm:1.3.5"
|
||||
dependencies:
|
||||
"@types/react": "npm:*"
|
||||
checksum: a21fe7f790d8bd2ae385829e5d5c369788c2facfc0a9f888dc1f07b4982398e02d1ffc8fca902cd77495094a598e0951e5c907edeb773abd1bf8a6db293d54a1
|
||||
checksum: 21599054dfa977ed8445b1ab3198a842531cb36bd620564c3f8a469688cbea051149eb644a99b2f8f6d02f8d9909a6145668f385781b5647601e9663de216946
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12832,15 +12832,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cosmiconfig@npm:^8.2.0":
|
||||
version: 8.2.0
|
||||
resolution: "cosmiconfig@npm:8.2.0"
|
||||
"cosmiconfig@npm:^8.2.0, cosmiconfig@npm:^8.3.5":
|
||||
version: 8.3.6
|
||||
resolution: "cosmiconfig@npm:8.3.6"
|
||||
dependencies:
|
||||
import-fresh: "npm:^3.2.1"
|
||||
import-fresh: "npm:^3.3.0"
|
||||
js-yaml: "npm:^4.1.0"
|
||||
parse-json: "npm:^5.0.0"
|
||||
parse-json: "npm:^5.2.0"
|
||||
path-type: "npm:^4.0.0"
|
||||
checksum: e0b188f9a672ee7135851bf9d9fc8f0ba00f9769c95fda5af0ebc274804f6aeb713b753e04e706f595e1fbd0fa67c5073840666019068c0296a06057560ab39d
|
||||
peerDependencies:
|
||||
typescript: ">=4.9.5"
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 91d082baca0f33b1c085bf010f9ded4af43cbedacba8821da0fb5667184d0a848addc52c31fadd080007f904a555319c238cf5f4c03e6d58ece2e4876b2e73d6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -17108,7 +17113,7 @@ __metadata:
|
||||
"@types/react": "npm:18.2.15"
|
||||
"@types/react-beautiful-dnd": "npm:13.1.8"
|
||||
"@types/react-dom": "npm:18.2.7"
|
||||
"@types/react-grid-layout": "npm:1.3.2"
|
||||
"@types/react-grid-layout": "npm:1.3.5"
|
||||
"@types/react-highlight-words": "npm:0.16.7"
|
||||
"@types/react-resizable": "npm:3.0.7"
|
||||
"@types/react-router-dom": "npm:5.3.3"
|
||||
@@ -17232,11 +17237,11 @@ __metadata:
|
||||
marked: "npm:5.1.1"
|
||||
marked-mangle: "npm:1.1.6"
|
||||
memoize-one: "npm:6.0.0"
|
||||
mini-css-extract-plugin: "npm:2.7.6"
|
||||
mini-css-extract-plugin: "npm:2.7.7"
|
||||
ml-regression-polynomial: "npm:^3.0.0"
|
||||
ml-regression-simple-linear: "npm:^3.0.0"
|
||||
moment: "npm:2.29.4"
|
||||
moment-timezone: "npm:0.5.43"
|
||||
moment-timezone: "npm:0.5.44"
|
||||
monaco-editor: "npm:0.34.0"
|
||||
monaco-promql: "npm:1.7.4"
|
||||
mousetrap: "npm:1.6.5"
|
||||
@@ -17249,13 +17254,13 @@ __metadata:
|
||||
node-forge: "npm:^1.3.1"
|
||||
node-notifier: "npm:10.0.1"
|
||||
ol: "npm:7.4.0"
|
||||
ol-ext: "npm:4.0.10"
|
||||
ol-ext: "npm:4.0.13"
|
||||
papaparse: "npm:5.4.1"
|
||||
pluralize: "npm:^8.0.0"
|
||||
postcss: "npm:8.4.31"
|
||||
postcss-loader: "npm:7.3.3"
|
||||
postcss: "npm:8.4.33"
|
||||
postcss-loader: "npm:7.3.4"
|
||||
postcss-reporter: "npm:7.0.5"
|
||||
postcss-scss: "npm:4.0.6"
|
||||
postcss-scss: "npm:4.0.9"
|
||||
prettier: "npm:3.0.0"
|
||||
prismjs: "npm:1.29.0"
|
||||
prop-types: "npm:15.8.1"
|
||||
@@ -17264,15 +17269,15 @@ __metadata:
|
||||
rc-drawer: "npm:6.5.2"
|
||||
rc-slider: "npm:10.3.1"
|
||||
rc-time-picker: "npm:3.7.3"
|
||||
rc-tree: "npm:5.8.0"
|
||||
re-resizable: "npm:6.9.9"
|
||||
rc-tree: "npm:5.8.2"
|
||||
re-resizable: "npm:6.9.11"
|
||||
react: "npm:18.2.0"
|
||||
react-beautiful-dnd: "npm:13.1.1"
|
||||
react-diff-viewer: "npm:^3.1.1"
|
||||
react-dom: "npm:18.2.0"
|
||||
react-draggable: "npm:4.4.5"
|
||||
react-draggable: "npm:4.4.6"
|
||||
react-dropzone: "npm:^14.2.3"
|
||||
react-grid-layout: "npm:1.4.2"
|
||||
react-grid-layout: "npm:1.4.4"
|
||||
react-highlight-words: "npm:0.20.0"
|
||||
react-hook-form: "npm:^7.49.2"
|
||||
react-i18next: "npm:^12.0.0"
|
||||
@@ -18177,7 +18182,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1":
|
||||
"import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0":
|
||||
version: 3.3.0
|
||||
resolution: "import-fresh@npm:3.3.0"
|
||||
dependencies:
|
||||
@@ -19786,12 +19791,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jiti@npm:^1.18.2":
|
||||
version: 1.19.1
|
||||
resolution: "jiti@npm:1.19.1"
|
||||
"jiti@npm:^1.20.0":
|
||||
version: 1.21.0
|
||||
resolution: "jiti@npm:1.21.0"
|
||||
bin:
|
||||
jiti: bin/jiti.js
|
||||
checksum: 88f11b406a877db348b956eb9cecf42d6cb6edcd6317aa75c032130fc88c1150baf8def5511e808af10b11336db2639a295ffe1ed455d65216dcef45a0a424b5
|
||||
checksum: 005a0239e50381b5c9919f59dbab86128367bd64872f3376dbbde54b6523f41bd134bf22909e2a509e38fd87e1c22125ca255b9b6b53e7df0fedd23f737334cc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -21269,14 +21274,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mini-css-extract-plugin@npm:2.7.6":
|
||||
version: 2.7.6
|
||||
resolution: "mini-css-extract-plugin@npm:2.7.6"
|
||||
"mini-css-extract-plugin@npm:2.7.7":
|
||||
version: 2.7.7
|
||||
resolution: "mini-css-extract-plugin@npm:2.7.7"
|
||||
dependencies:
|
||||
schema-utils: "npm:^4.0.0"
|
||||
peerDependencies:
|
||||
webpack: ^5.0.0
|
||||
checksum: 1f718bfdcb7c2bf5e4336f694e5576432149d63f9dacaf94eae38ad046534050471a712a2d1bedf95e1722a2d3b56c3361d7352849e802e4875e716885e952c3
|
||||
checksum: 55e9cb89a648e9d493dd66eb54cbf02783c6623d0e332ddb4c2374891fcd8de051d5f97473e40478eb2e091ea32fc9ac84c3cd583b445df9f444cc6b7483e1b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -21633,12 +21638,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"moment-timezone@npm:0.5.43":
|
||||
version: 0.5.43
|
||||
resolution: "moment-timezone@npm:0.5.43"
|
||||
"moment-timezone@npm:0.5.44":
|
||||
version: 0.5.44
|
||||
resolution: "moment-timezone@npm:0.5.44"
|
||||
dependencies:
|
||||
moment: "npm:^2.29.4"
|
||||
checksum: f8b66f8562960d6c2ec90ea7e2ca8c10bd5f5cf5ced2eaaac83deb1011b145d0154e8d77018cf5e913d489898a343122a3d815768809653ab039306dce1db1eb
|
||||
checksum: 72f0a425c52a858cca76d5ab35dfb02d68ac7292b60e4b34fb432c1fae07e32ad7f67de7ff5d2d2c868eee8052c9981fc3244f5fc9a2f454fc4fd15db2ec45a7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -21850,12 +21855,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.3.6":
|
||||
version: 3.3.6
|
||||
resolution: "nanoid@npm:3.3.6"
|
||||
"nanoid@npm:^3.3.7":
|
||||
version: 3.3.7
|
||||
resolution: "nanoid@npm:3.3.7"
|
||||
bin:
|
||||
nanoid: bin/nanoid.cjs
|
||||
checksum: 67235c39d1bc05851383dadde5cf77ae1c90c2a1d189e845c7f20f646f0488d875ad5f5226bbba072a88cebbb085a3f784a6673117daf785bdf614a852550362
|
||||
checksum: ac1eb60f615b272bccb0e2b9cd933720dad30bf9708424f691b8113826bb91aca7e9d14ef5d9415a6ba15c266b37817256f58d8ce980c82b0ba3185352565679
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -22510,12 +22515,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ol-ext@npm:4.0.10":
|
||||
version: 4.0.10
|
||||
resolution: "ol-ext@npm:4.0.10"
|
||||
"ol-ext@npm:4.0.13":
|
||||
version: 4.0.13
|
||||
resolution: "ol-ext@npm:4.0.13"
|
||||
peerDependencies:
|
||||
ol: ">= 5.3.0"
|
||||
checksum: 11a799c71ef53a75a7aea6bfbe43384ae4ffe1b9d5f4dac950213da165f72e33555a10f2929a6197bf49eeb25d4721a67e4798fdcbf5da519d7ad9bedae770ee
|
||||
checksum: 656eea9d354296406a48590a69246433a6ae0233f2b28d77f0a6f954a53bf4b54d42a54ff50a0395b382044e7ef912f58687e0538e79a9ff0cfe2f22adf0f718
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23365,17 +23370,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-loader@npm:7.3.3":
|
||||
version: 7.3.3
|
||||
resolution: "postcss-loader@npm:7.3.3"
|
||||
"postcss-loader@npm:7.3.4":
|
||||
version: 7.3.4
|
||||
resolution: "postcss-loader@npm:7.3.4"
|
||||
dependencies:
|
||||
cosmiconfig: "npm:^8.2.0"
|
||||
jiti: "npm:^1.18.2"
|
||||
semver: "npm:^7.3.8"
|
||||
cosmiconfig: "npm:^8.3.5"
|
||||
jiti: "npm:^1.20.0"
|
||||
semver: "npm:^7.5.4"
|
||||
peerDependencies:
|
||||
postcss: ^7.0.0 || ^8.0.1
|
||||
webpack: ^5.0.0
|
||||
checksum: 743a4286db68169d271bef31e6e9351874bcf2dfa408b82c648c2d5bfba9c862cbfe3004494d927469654d6ac8b82fe647f2b80a186c1dbd44d81632eec1e838
|
||||
checksum: 234b01149a966a6190290c6d265b8e3df10f43262dd679451c1e7370bae74e27b746b02e660d204b901e3cf1ad28759c2679a93c64a3eb499169d8dec39df1c1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23665,12 +23670,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss-scss@npm:4.0.6, postcss-scss@npm:^4.0.6":
|
||||
version: 4.0.6
|
||||
resolution: "postcss-scss@npm:4.0.6"
|
||||
"postcss-scss@npm:4.0.9, postcss-scss@npm:^4.0.6":
|
||||
version: 4.0.9
|
||||
resolution: "postcss-scss@npm:4.0.9"
|
||||
peerDependencies:
|
||||
postcss: ^8.4.19
|
||||
checksum: fa436a2c92555135f07135586798089b4d8b31ad9d7f3352563e5c9b722e8ff64c32f95071541f87bc78b63dc6dd1475a68c4683b05e5618aaf55adfc3b007d7
|
||||
postcss: ^8.4.29
|
||||
checksum: d191c771344357a28995a2f53041ec699070331b8238e076001cedde97215bd3ebf596d0a9882b22c566977c4b72816dafc00028dc09153c6f97f71cd28a70f7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23714,14 +23719,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:8.4.31, postcss@npm:^8.4.21, postcss@npm:^8.4.24, postcss@npm:^8.4.28":
|
||||
version: 8.4.31
|
||||
resolution: "postcss@npm:8.4.31"
|
||||
"postcss@npm:8.4.33, postcss@npm:^8.4.21, postcss@npm:^8.4.24, postcss@npm:^8.4.28":
|
||||
version: 8.4.33
|
||||
resolution: "postcss@npm:8.4.33"
|
||||
dependencies:
|
||||
nanoid: "npm:^3.3.6"
|
||||
nanoid: "npm:^3.3.7"
|
||||
picocolors: "npm:^1.0.0"
|
||||
source-map-js: "npm:^1.0.2"
|
||||
checksum: 1a6653e72105907377f9d4f2cd341d8d90e3fde823a5ddea1e2237aaa56933ea07853f0f2758c28892a1d70c53bbaca200eb8b80f8ed55f13093003dbec5afa0
|
||||
checksum: e22a4594c255f26117f38419fb494d7ecab0f596cd409f7aadc8a6173abf180ed7ea970cd13fd366ab12b5840be901d2a09b25197700c2ebcb5a8077326bf519
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -24413,23 +24418,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rc-tree@npm:5.8.0":
|
||||
version: 5.8.0
|
||||
resolution: "rc-tree@npm:5.8.0"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.10.1"
|
||||
classnames: "npm:2.x"
|
||||
rc-motion: "npm:^2.0.1"
|
||||
rc-util: "npm:^5.16.1"
|
||||
rc-virtual-list: "npm:^3.5.1"
|
||||
peerDependencies:
|
||||
react: "*"
|
||||
react-dom: "*"
|
||||
checksum: 130cc89f582aedbe252590f8078bd39535ed2de43fa7bf3be88695065d15405768340fb26dda12d90601296afd439d876ce859d4604659a6adde4f783ff7f26a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rc-tree@npm:~5.8.1":
|
||||
"rc-tree@npm:5.8.2, rc-tree@npm:~5.8.1":
|
||||
version: 5.8.2
|
||||
resolution: "rc-tree@npm:5.8.2"
|
||||
dependencies:
|
||||
@@ -24501,13 +24490,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"re-resizable@npm:6.9.9":
|
||||
version: 6.9.9
|
||||
resolution: "re-resizable@npm:6.9.9"
|
||||
"re-resizable@npm:6.9.11":
|
||||
version: 6.9.11
|
||||
resolution: "re-resizable@npm:6.9.11"
|
||||
peerDependencies:
|
||||
react: ^16.13.1 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0
|
||||
checksum: c2e22bfeca9765b75d33b0e9135b0034684c6fa6b11927d81e464f70b7c5cf20176fd52d128ab3ee342ca0271357efdca2c4856a80e174b58101349bba35c11c
|
||||
checksum: 3f2590374d03a6b5f7ebc26f273bdb461ceecb1322adf3b7925c695b1c6fa0841225feb79cf63d19c3c2451a9a8ed4f4a82fa119f3a22565ce78e0619d2b624c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -24675,20 +24664,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-draggable@npm:4.4.5":
|
||||
version: 4.4.5
|
||||
resolution: "react-draggable@npm:4.4.5"
|
||||
dependencies:
|
||||
clsx: "npm:^1.1.1"
|
||||
prop-types: "npm:^15.8.1"
|
||||
peerDependencies:
|
||||
react: ">= 16.3.0"
|
||||
react-dom: ">= 16.3.0"
|
||||
checksum: a6af765a71a62636a9c12868e9015e86e0160fbd9218a895200813f502e9ff848b8aab16ff7177faa808dd5a498b8a03c355edd2be0b823ec1035e91edf2103d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-draggable@npm:^4.0.0, react-draggable@npm:^4.0.3, react-draggable@npm:^4.4.5":
|
||||
"react-draggable@npm:4.4.6, react-draggable@npm:^4.0.0, react-draggable@npm:^4.0.3, react-draggable@npm:^4.4.5":
|
||||
version: 4.4.6
|
||||
resolution: "react-draggable@npm:4.4.6"
|
||||
dependencies:
|
||||
@@ -24780,9 +24756,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-grid-layout@npm:1.4.2":
|
||||
version: 1.4.2
|
||||
resolution: "react-grid-layout@npm:1.4.2"
|
||||
"react-grid-layout@npm:1.4.4":
|
||||
version: 1.4.4
|
||||
resolution: "react-grid-layout@npm:1.4.4"
|
||||
dependencies:
|
||||
clsx: "npm:^2.0.0"
|
||||
fast-equals: "npm:^4.0.3"
|
||||
@@ -24793,7 +24769,7 @@ __metadata:
|
||||
peerDependencies:
|
||||
react: ">= 16.3.0"
|
||||
react-dom: ">= 16.3.0"
|
||||
checksum: 651819b1b5af8d57cd7ee702fa367f43e00c77be6403b5ab423179207be340a01d8bf9cb61e76fefafec40a8aacbcb2f47efb902eef77eedb6ce56f3716a3408
|
||||
checksum: 43c9bc7e8ce5a902720ebe559b1551724615c617391b040a0a275597748e1927b293c5ddcb73053b1925909e1c293948b4969a35799d541bf9d8886e07ac0085
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -24811,11 +24787,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"react-hook-form@npm:^7.49.2":
|
||||
version: 7.49.2
|
||||
resolution: "react-hook-form@npm:7.49.2"
|
||||
version: 7.49.3
|
||||
resolution: "react-hook-form@npm:7.49.3"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17 || ^18
|
||||
checksum: 7895d65b8458c42d46eb338803bb0fd1aab42fc69ecf80b47846eace9493a10cac5b05c9b744a5f9f1f7969a3e2703fc2118cdab97e49a7798a72d09f106383f
|
||||
checksum: ccd35d73f26b56c2933312c5d445552aab8c20a02ab07360808f039c9daf37a046a0afa2c2c56fac0a16b1963ae3e4ebce37136d14388002d00d6c5483e3fdb7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user