@spinnaker/kayenta 0.0.0-2025.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +9 -0
- package/.eslintrc.js +1 -0
- package/.huskyrc +5 -0
- package/.lintstagedrc.json +4 -0
- package/.prettierignore +4 -0
- package/.prettierrc.js +1 -0
- package/LICENSE.txt +203 -0
- package/README.md +81 -0
- package/__mocks__/styleMock.js +1 -0
- package/__mocks__/version.json +4 -0
- package/babel.config.js +3 -0
- package/build.gradle +67 -0
- package/build_scripts/checkLicenses.js +79 -0
- package/gradle.properties +0 -0
- package/jest.config.js +204 -0
- package/jest.setup.js +5 -0
- package/package.json +166 -0
- package/rollup-plugin-angularjs-template-loader.js +82 -0
- package/rollup.config.js +30 -0
- package/src/index.ts +2 -0
- package/src/kayenta/actions/creators.ts +163 -0
- package/src/kayenta/actions/index.ts +98 -0
- package/src/kayenta/canary.dataSource.bridge.ts +53 -0
- package/src/kayenta/canary.dataSource.stub.ts +64 -0
- package/src/kayenta/canary.help.ts +136 -0
- package/src/kayenta/canary.less +168 -0
- package/src/kayenta/canary.settings.ts +26 -0
- package/src/kayenta/canary.tsx +67 -0
- package/src/kayenta/components/canaryScore.component.less +77 -0
- package/src/kayenta/components/canaryScore.component.ts +12 -0
- package/src/kayenta/components/canaryScore.tsx +63 -0
- package/src/kayenta/components/canaryScores.component.ts +20 -0
- package/src/kayenta/components/canaryScores.less +22 -0
- package/src/kayenta/components/canaryScores.tsx +163 -0
- package/src/kayenta/components/loadStates.tsx +52 -0
- package/src/kayenta/domain/ICanaryConfig.ts +57 -0
- package/src/kayenta/domain/ICanaryConfigSummary.ts +7 -0
- package/src/kayenta/domain/ICanaryConfigUpdateResponse.ts +3 -0
- package/src/kayenta/domain/ICanaryExecutionStatusResult.ts +72 -0
- package/src/kayenta/domain/ICanaryJudgeResult.ts +51 -0
- package/src/kayenta/domain/ICanaryJudgeResultSummary.ts +5 -0
- package/src/kayenta/domain/ICanaryScoreThresholds.ts +4 -0
- package/src/kayenta/domain/IJudge.ts +5 -0
- package/src/kayenta/domain/IKayentaAccount.ts +14 -0
- package/src/kayenta/domain/IKayentaStageConfig.ts +58 -0
- package/src/kayenta/domain/IMetricSetPair.ts +17 -0
- package/src/kayenta/domain/IMetricsServiceMetadata.ts +2 -0
- package/src/kayenta/domain/ISetupCanaryStage.ts +11 -0
- package/src/kayenta/domain/MetricClassificationLabel.ts +8 -0
- package/src/kayenta/domain/ScoreClassificationLabel.ts +7 -0
- package/src/kayenta/domain/index.ts +15 -0
- package/src/kayenta/edit/changeMetricGroupModal.tsx +107 -0
- package/src/kayenta/edit/configDetail.tsx +31 -0
- package/src/kayenta/edit/configDetailActionButtons.tsx +24 -0
- package/src/kayenta/edit/configDetailHeader.tsx +104 -0
- package/src/kayenta/edit/configDetailLoadStates.tsx +36 -0
- package/src/kayenta/edit/configDetailLoader.tsx +60 -0
- package/src/kayenta/edit/configJson.less +42 -0
- package/src/kayenta/edit/configJsonModal.tsx +158 -0
- package/src/kayenta/edit/configList.less +7 -0
- package/src/kayenta/edit/configList.tsx +57 -0
- package/src/kayenta/edit/copyConfigButton.tsx +34 -0
- package/src/kayenta/edit/createConfigButton.tsx +34 -0
- package/src/kayenta/edit/deleteModal.tsx +87 -0
- package/src/kayenta/edit/edit.tsx +24 -0
- package/src/kayenta/edit/editMetricEffectSizes.tsx +186 -0
- package/src/kayenta/edit/editMetricModal.less +9 -0
- package/src/kayenta/edit/editMetricModal.spec.tsx +129 -0
- package/src/kayenta/edit/editMetricModal.tsx +294 -0
- package/src/kayenta/edit/editMetricValidation.spec.ts +63 -0
- package/src/kayenta/edit/editMetricValidation.ts +50 -0
- package/src/kayenta/edit/filterTemplateSelector.less +15 -0
- package/src/kayenta/edit/filterTemplateSelector.spec.tsx +106 -0
- package/src/kayenta/edit/filterTemplateSelector.tsx +194 -0
- package/src/kayenta/edit/filterTemplatesValidation.spec.ts +108 -0
- package/src/kayenta/edit/filterTemplatesValidation.ts +95 -0
- package/src/kayenta/edit/footer.less +30 -0
- package/src/kayenta/edit/footer.tsx +12 -0
- package/src/kayenta/edit/groupName.tsx +80 -0
- package/src/kayenta/edit/groupTabs.tsx +106 -0
- package/src/kayenta/edit/groupWeight.tsx +80 -0
- package/src/kayenta/edit/groupWeights.tsx +38 -0
- package/src/kayenta/edit/inlineTemplateEditor.spec.tsx +42 -0
- package/src/kayenta/edit/inlineTemplateEditor.tsx +61 -0
- package/src/kayenta/edit/judgeSelect.tsx +82 -0
- package/src/kayenta/edit/metricConfigurerDelegator.tsx +30 -0
- package/src/kayenta/edit/metricList.less +21 -0
- package/src/kayenta/edit/metricList.tsx +215 -0
- package/src/kayenta/edit/metricStoreSelector.tsx +66 -0
- package/src/kayenta/edit/nameAndDescription.tsx +90 -0
- package/src/kayenta/edit/openConfigJsonModalButton.tsx +33 -0
- package/src/kayenta/edit/openDeleteModalButton.tsx +50 -0
- package/src/kayenta/edit/ownedBy.tsx +34 -0
- package/src/kayenta/edit/save.tsx +19 -0
- package/src/kayenta/edit/saveConfigButton.tsx +65 -0
- package/src/kayenta/edit/saveConfigError.tsx +59 -0
- package/src/kayenta/edit/scoring.tsx +35 -0
- package/src/kayenta/edit/selectConfig.tsx +10 -0
- package/src/kayenta/edit/validationErrors.tsx +39 -0
- package/src/kayenta/index.ts +6 -0
- package/src/kayenta/layout/addNewButton.tsx +20 -0
- package/src/kayenta/layout/centeredDetail.tsx +13 -0
- package/src/kayenta/layout/deleteButton.tsx +11 -0
- package/src/kayenta/layout/disableable.tsx +87 -0
- package/src/kayenta/layout/formList.tsx +26 -0
- package/src/kayenta/layout/formRow.tsx +36 -0
- package/src/kayenta/layout/formattedDate.tsx +14 -0
- package/src/kayenta/layout/index.ts +2 -0
- package/src/kayenta/layout/keyValueList.less +20 -0
- package/src/kayenta/layout/keyValueList.tsx +114 -0
- package/src/kayenta/layout/list.less +9 -0
- package/src/kayenta/layout/list.spec.tsx +83 -0
- package/src/kayenta/layout/list.tsx +73 -0
- package/src/kayenta/layout/listDetail.tsx +33 -0
- package/src/kayenta/layout/radioChoice.tsx +29 -0
- package/src/kayenta/layout/styleguide.tsx +16 -0
- package/src/kayenta/layout/table/index.ts +5 -0
- package/src/kayenta/layout/table/nativeTable.tsx +51 -0
- package/src/kayenta/layout/table/nativeTableHeader.tsx +26 -0
- package/src/kayenta/layout/table/table.tsx +56 -0
- package/src/kayenta/layout/table/tableColumn.ts +7 -0
- package/src/kayenta/layout/table/tableHeader.tsx +23 -0
- package/src/kayenta/layout/tabs.tsx +26 -0
- package/src/kayenta/layout/titledSection.less +16 -0
- package/src/kayenta/layout/titledSection.tsx +20 -0
- package/src/kayenta/layout/titledSubsection.less +11 -0
- package/src/kayenta/layout/titledSubsection.tsx +22 -0
- package/src/kayenta/manualAnalysis/ManualAnalysisModal.tsx +716 -0
- package/src/kayenta/metricStore/atlas/atlasMetricConfigurer.tsx +130 -0
- package/src/kayenta/metricStore/atlas/index.ts +8 -0
- package/src/kayenta/metricStore/datadog/domain/IDatadogMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/datadog/index.ts +9 -0
- package/src/kayenta/metricStore/datadog/metricConfigurer.tsx +90 -0
- package/src/kayenta/metricStore/datadog/metricTypeSelector.spec.tsx +59 -0
- package/src/kayenta/metricStore/datadog/metricTypeSelector.tsx +73 -0
- package/src/kayenta/metricStore/graphite/domain/IGraphiteMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/graphite/index.ts +8 -0
- package/src/kayenta/metricStore/graphite/metricConfigurer.tsx +54 -0
- package/src/kayenta/metricStore/graphite/metricTypeSelector.tsx +80 -0
- package/src/kayenta/metricStore/graphite/typeahead.less +3 -0
- package/src/kayenta/metricStore/index.ts +8 -0
- package/src/kayenta/metricStore/metricStoreConfig.service.ts +12 -0
- package/src/kayenta/metricStore/newrelic/domain/INewRelicMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/newrelic/index.ts +8 -0
- package/src/kayenta/metricStore/newrelic/metricConfigurer.tsx +58 -0
- package/src/kayenta/metricStore/prometheus/domain/IPrometheusCanaryMetricSetQueryConfig.ts +14 -0
- package/src/kayenta/metricStore/prometheus/domain/IPrometheusMetricDescriptor.ts +5 -0
- package/src/kayenta/metricStore/prometheus/index.ts +12 -0
- package/src/kayenta/metricStore/prometheus/metricConfigurer.tsx +157 -0
- package/src/kayenta/metricStore/prometheus/metricTypeSelector.less +5 -0
- package/src/kayenta/metricStore/prometheus/metricTypeSelector.spec.tsx +62 -0
- package/src/kayenta/metricStore/prometheus/metricTypeSelector.tsx +144 -0
- package/src/kayenta/metricStore/prometheus/queryTypeSelectors.spec.ts +61 -0
- package/src/kayenta/metricStore/prometheus/queryTypeSelectors.ts +38 -0
- package/src/kayenta/metricStore/signalfx/domain/ISignalFxCanaryMetricSetQueryConfig.ts +7 -0
- package/src/kayenta/metricStore/signalfx/index.ts +8 -0
- package/src/kayenta/metricStore/signalfx/metricConfigurer.less +10 -0
- package/src/kayenta/metricStore/signalfx/metricConfigurer.tsx +187 -0
- package/src/kayenta/metricStore/stackdriver/domain/IStackdriverCanaryMetricSetQueryConfig.ts +9 -0
- package/src/kayenta/metricStore/stackdriver/domain/IStackdriverMetricDescriptor.ts +17 -0
- package/src/kayenta/metricStore/stackdriver/index.ts +12 -0
- package/src/kayenta/metricStore/stackdriver/metricConfigurer.tsx +144 -0
- package/src/kayenta/metricStore/stackdriver/metricTypeSelector.spec.tsx +92 -0
- package/src/kayenta/metricStore/stackdriver/metricTypeSelector.tsx +113 -0
- package/src/kayenta/middleware/actionInterceptor.ts +29 -0
- package/src/kayenta/middleware/asyncDispatch.ts +37 -0
- package/src/kayenta/middleware/epics.ts +211 -0
- package/src/kayenta/middleware/index.ts +3 -0
- package/src/kayenta/navigation/canary.states.stub.ts +28 -0
- package/src/kayenta/navigation/canary.states.ts +182 -0
- package/src/kayenta/reducers/app.ts +56 -0
- package/src/kayenta/reducers/asyncRequest.ts +5 -0
- package/src/kayenta/reducers/data.ts +169 -0
- package/src/kayenta/reducers/editingTemplate.ts +54 -0
- package/src/kayenta/reducers/group.ts +82 -0
- package/src/kayenta/reducers/index.ts +245 -0
- package/src/kayenta/reducers/prometheusMetricConfig.spec.ts +33 -0
- package/src/kayenta/reducers/prometheusMetricConfig.ts +56 -0
- package/src/kayenta/reducers/selectedConfig.spec.ts +190 -0
- package/src/kayenta/reducers/selectedConfig.ts +566 -0
- package/src/kayenta/reducers/selectedRun.ts +101 -0
- package/src/kayenta/reducers/signalFxMetricConfig.ts +36 -0
- package/src/kayenta/reducers/stackdriverMetricConfig.spec.ts +33 -0
- package/src/kayenta/reducers/stackdriverMetricConfig.ts +41 -0
- package/src/kayenta/reducers/templates.spec.ts +192 -0
- package/src/kayenta/reducers/validators.ts +118 -0
- package/src/kayenta/report/detail/allMetricResultsHeader.tsx +32 -0
- package/src/kayenta/report/detail/clickableHeader.tsx +21 -0
- package/src/kayenta/report/detail/colors.ts +47 -0
- package/src/kayenta/report/detail/detail.less +16 -0
- package/src/kayenta/report/detail/detail.tsx +48 -0
- package/src/kayenta/report/detail/detailLoader.tsx +55 -0
- package/src/kayenta/report/detail/graph/graph.tsx +37 -0
- package/src/kayenta/report/detail/graph/metricSetPairGraph.service.ts +35 -0
- package/src/kayenta/report/detail/graph/semiotic/boxplot.less +45 -0
- package/src/kayenta/report/detail/graph/semiotic/boxplot.tsx +283 -0
- package/src/kayenta/report/detail/graph/semiotic/chartHeader.tsx +19 -0
- package/src/kayenta/report/detail/graph/semiotic/chartLegend.less +26 -0
- package/src/kayenta/report/detail/graph/semiotic/chartLegend.tsx +42 -0
- package/src/kayenta/report/detail/graph/semiotic/circleIcon.tsx +16 -0
- package/src/kayenta/report/detail/graph/semiotic/config.less +5 -0
- package/src/kayenta/report/detail/graph/semiotic/config.ts +38 -0
- package/src/kayenta/report/detail/graph/semiotic/customAxisTickLabel.tsx +17 -0
- package/src/kayenta/report/detail/graph/semiotic/declarations/labella.d.ts +16 -0
- package/src/kayenta/report/detail/graph/semiotic/declarations/react-container-dimensions.d.ts +3 -0
- package/src/kayenta/report/detail/graph/semiotic/declarations/semiotic.d.ts +160 -0
- package/src/kayenta/report/detail/graph/semiotic/differenceArea.less +17 -0
- package/src/kayenta/report/detail/graph/semiotic/differenceArea.tsx +186 -0
- package/src/kayenta/report/detail/graph/semiotic/histogram.less +22 -0
- package/src/kayenta/report/detail/graph/semiotic/histogram.tsx +251 -0
- package/src/kayenta/report/detail/graph/semiotic/index.tsx +19 -0
- package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.less +5 -0
- package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.tsx +10 -0
- package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.less +6 -0
- package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.tsx +58 -0
- package/src/kayenta/report/detail/graph/semiotic/semiotic.service.ts +32 -0
- package/src/kayenta/report/detail/graph/semiotic/semioticGraph.less +53 -0
- package/src/kayenta/report/detail/graph/semiotic/semioticGraph.tsx +49 -0
- package/src/kayenta/report/detail/graph/semiotic/timeSeries.less +42 -0
- package/src/kayenta/report/detail/graph/semiotic/timeSeries.tsx +473 -0
- package/src/kayenta/report/detail/graph/semiotic/tooltip.tsx +55 -0
- package/src/kayenta/report/detail/graph/semiotic/utils.ts +90 -0
- package/src/kayenta/report/detail/graphTypeSelector.less +4 -0
- package/src/kayenta/report/detail/graphTypeSelector.tsx +50 -0
- package/src/kayenta/report/detail/groupScores.tsx +68 -0
- package/src/kayenta/report/detail/header.less +70 -0
- package/src/kayenta/report/detail/header.tsx +39 -0
- package/src/kayenta/report/detail/headerArrow.tsx +13 -0
- package/src/kayenta/report/detail/loadStates.tsx +31 -0
- package/src/kayenta/report/detail/metricResultActions.less +29 -0
- package/src/kayenta/report/detail/metricResultActions.tsx +87 -0
- package/src/kayenta/report/detail/metricResultClassification.tsx +22 -0
- package/src/kayenta/report/detail/metricResultDetail.tsx +20 -0
- package/src/kayenta/report/detail/metricResultDetailLayout.tsx +19 -0
- package/src/kayenta/report/detail/metricResultDeviation.tsx +25 -0
- package/src/kayenta/report/detail/metricResultStats.less +9 -0
- package/src/kayenta/report/detail/metricResultStats.tsx +120 -0
- package/src/kayenta/report/detail/metricResults.less +12 -0
- package/src/kayenta/report/detail/metricResults.tsx +52 -0
- package/src/kayenta/report/detail/metricResultsClassificationFilters.tsx +65 -0
- package/src/kayenta/report/detail/metricResultsColumns.tsx +27 -0
- package/src/kayenta/report/detail/metricResultsList.less +44 -0
- package/src/kayenta/report/detail/metricResultsList.tsx +120 -0
- package/src/kayenta/report/detail/metricSetPairLoadStates.tsx +22 -0
- package/src/kayenta/report/detail/multipleResultsTable.tsx +81 -0
- package/src/kayenta/report/detail/reportException.tsx +57 -0
- package/src/kayenta/report/detail/reportExplanation.less +12 -0
- package/src/kayenta/report/detail/reportExplanation.tsx +32 -0
- package/src/kayenta/report/detail/reportMetadata.tsx +167 -0
- package/src/kayenta/report/detail/reportScores.less +47 -0
- package/src/kayenta/report/detail/reportScores.tsx +80 -0
- package/src/kayenta/report/detail/score.tsx +33 -0
- package/src/kayenta/report/detail/sourceLinks.tsx +69 -0
- package/src/kayenta/report/list/configLink.tsx +32 -0
- package/src/kayenta/report/list/executionList.less +7 -0
- package/src/kayenta/report/list/loadStates.tsx +32 -0
- package/src/kayenta/report/list/pipelineLink.tsx +15 -0
- package/src/kayenta/report/list/reportLink.tsx +33 -0
- package/src/kayenta/report/list/table.tsx +309 -0
- package/src/kayenta/report/report.tsx +11 -0
- package/src/kayenta/selectors/filterTemplatesSelectors.ts +87 -0
- package/src/kayenta/selectors/index.ts +62 -0
- package/src/kayenta/service/canaryConfig.service.ts +122 -0
- package/src/kayenta/service/canaryRun.service.ts +60 -0
- package/src/kayenta/service/delegateFactory.ts +24 -0
- package/src/kayenta/service/metricsServiceMetadata.service.ts +9 -0
- package/src/kayenta/stages/kayentaStage/AnalysisType.spec.tsx +47 -0
- package/src/kayenta/stages/kayentaStage/AnalysisType.tsx +49 -0
- package/src/kayenta/stages/kayentaStage/CanaryExecutionLabel.tsx +26 -0
- package/src/kayenta/stages/kayentaStage/analysisType.component.ts +12 -0
- package/src/kayenta/stages/kayentaStage/canaryRunSummaries.component.ts +12 -0
- package/src/kayenta/stages/kayentaStage/canaryRunSummaries.less +5 -0
- package/src/kayenta/stages/kayentaStage/canaryRunSummaries.tsx +136 -0
- package/src/kayenta/stages/kayentaStage/forAnalysisType.component.ts +45 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.controller.ts +789 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.html +528 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.less +5 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.transformer.ts +179 -0
- package/src/kayenta/stages/kayentaStage/kayentaStage.ts +221 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageConfigSection.component.ts +21 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.controller.ts +88 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.html +114 -0
- package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.less +6 -0
- package/src/kayenta/stages/kayentaStage/stageTypes.ts +5 -0
- package/src/kayenta/utils/duration.spec.ts +69 -0
- package/src/kayenta/utils/duration.ts +48 -0
- package/src/lazy.ts +29 -0
- package/src/stub.ts +60 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as Creators from 'kayenta/actions/creators';
|
|
2
|
+
import { ICanaryMetricConfig } from 'kayenta/domain/ICanaryConfig';
|
|
3
|
+
import { DISABLE_EDIT_CONFIG, DisableableReactSelect } from 'kayenta/layout/disableable';
|
|
4
|
+
import FormRow from 'kayenta/layout/formRow';
|
|
5
|
+
import { IUpdateListPayload, List } from 'kayenta/layout/list';
|
|
6
|
+
import RadioChoice from 'kayenta/layout/radioChoice';
|
|
7
|
+
import { ICanaryState } from 'kayenta/reducers';
|
|
8
|
+
import { queryTypeSelector } from 'kayenta/selectors/filterTemplatesSelectors';
|
|
9
|
+
import { get } from 'lodash';
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import { connect } from 'react-redux';
|
|
12
|
+
import { Option } from 'react-select';
|
|
13
|
+
import { Action } from 'redux';
|
|
14
|
+
import { createSelector } from 'reselect';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
IPrometheusCanaryMetricSetQueryConfig,
|
|
18
|
+
PrometheusQueryType,
|
|
19
|
+
} from './domain/IPrometheusCanaryMetricSetQueryConfig';
|
|
20
|
+
import { ICanaryMetricValidationErrors } from '../../edit/editMetricValidation';
|
|
21
|
+
import PrometheusMetricTypeSelector from './metricTypeSelector';
|
|
22
|
+
import { editingMetricSelector } from '../../selectors';
|
|
23
|
+
|
|
24
|
+
interface IPrometheusMetricConfigurerStateProps {
|
|
25
|
+
editingMetric: ICanaryMetricConfig;
|
|
26
|
+
queryType: PrometheusQueryType;
|
|
27
|
+
validationErrors: ICanaryMetricValidationErrors;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface IPrometheusMetricConfigurerDispatchProps {
|
|
31
|
+
updateLabelBindings: (payload: IUpdateListPayload) => void;
|
|
32
|
+
updateGroupBy: (payload: IUpdateListPayload) => void;
|
|
33
|
+
updateFilterQueryType: (queryType: PrometheusQueryType) => void;
|
|
34
|
+
updatePrometheusMetricQueryField: <T extends keyof IPrometheusCanaryMetricSetQueryConfig>(
|
|
35
|
+
field: keyof IPrometheusCanaryMetricSetQueryConfig,
|
|
36
|
+
value: Option<IPrometheusCanaryMetricSetQueryConfig[T]>,
|
|
37
|
+
) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const RESOURCE_TYPES = ['gce_instance', 'aws_ec2_instance'];
|
|
41
|
+
|
|
42
|
+
const toReactSelectOptions = (values: string[]): Array<Option<string>> =>
|
|
43
|
+
values.map((value) => ({ value, label: value }));
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* Component for configuring a Prometheus metric.
|
|
47
|
+
* */
|
|
48
|
+
function PrometheusMetricConfigurer({
|
|
49
|
+
editingMetric,
|
|
50
|
+
queryType,
|
|
51
|
+
updateLabelBindings,
|
|
52
|
+
updateGroupBy,
|
|
53
|
+
updateFilterQueryType,
|
|
54
|
+
updatePrometheusMetricQueryField,
|
|
55
|
+
validationErrors,
|
|
56
|
+
}: IPrometheusMetricConfigurerStateProps & IPrometheusMetricConfigurerDispatchProps) {
|
|
57
|
+
return (
|
|
58
|
+
<>
|
|
59
|
+
<FormRow label="Query Type" helpId="canary.config.prometheus.queryType">
|
|
60
|
+
<RadioChoice
|
|
61
|
+
value={PrometheusQueryType.DEFAULT}
|
|
62
|
+
label="Default"
|
|
63
|
+
name="queryType"
|
|
64
|
+
current={queryType}
|
|
65
|
+
action={() => updateFilterQueryType(PrometheusQueryType.DEFAULT)}
|
|
66
|
+
/>
|
|
67
|
+
<RadioChoice
|
|
68
|
+
value={PrometheusQueryType.PROMQL}
|
|
69
|
+
label="PromQL"
|
|
70
|
+
name="queryType"
|
|
71
|
+
current={queryType}
|
|
72
|
+
action={() => updateFilterQueryType(PrometheusQueryType.PROMQL)}
|
|
73
|
+
/>
|
|
74
|
+
</FormRow>
|
|
75
|
+
{queryType === PrometheusQueryType.DEFAULT && (
|
|
76
|
+
<>
|
|
77
|
+
<FormRow label="Resource Type" inputOnly={true}>
|
|
78
|
+
<DisableableReactSelect
|
|
79
|
+
value={get(editingMetric, 'query.resourceType')}
|
|
80
|
+
options={toReactSelectOptions(RESOURCE_TYPES)}
|
|
81
|
+
onChange={(option: Option<string>) => updatePrometheusMetricQueryField('resourceType', option)}
|
|
82
|
+
disabledStateKeys={[DISABLE_EDIT_CONFIG]}
|
|
83
|
+
/>
|
|
84
|
+
</FormRow>
|
|
85
|
+
<FormRow label="Metric Name" inputOnly={true} error={get(validationErrors, 'metricName.message', null)}>
|
|
86
|
+
<PrometheusMetricTypeSelector
|
|
87
|
+
value={get(editingMetric, 'query.metricName', '')}
|
|
88
|
+
onChange={(option: Option<string>) => updatePrometheusMetricQueryField('metricName', option)}
|
|
89
|
+
/>
|
|
90
|
+
</FormRow>
|
|
91
|
+
<FormRow label="Label Bindings">
|
|
92
|
+
<List list={editingMetric.query.labelBindings || []} actionCreator={updateLabelBindings} />
|
|
93
|
+
</FormRow>
|
|
94
|
+
<FormRow label="Group By">
|
|
95
|
+
<List list={editingMetric.query.groupByFields || []} actionCreator={updateGroupBy} />
|
|
96
|
+
</FormRow>
|
|
97
|
+
</>
|
|
98
|
+
)}
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function mapStateToProps(state: ICanaryState): IPrometheusMetricConfigurerStateProps {
|
|
104
|
+
return {
|
|
105
|
+
editingMetric: state.selectedConfig.editingMetric,
|
|
106
|
+
queryType: queryTypeSelector(state),
|
|
107
|
+
validationErrors: prometheusMetricValidationSelector(state),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function mapDispatchToProps(dispatch: (action: Action & any) => void): IPrometheusMetricConfigurerDispatchProps {
|
|
112
|
+
return {
|
|
113
|
+
updateLabelBindings: (payload) => dispatch(Creators.updatePrometheusLabelBindings(payload)),
|
|
114
|
+
updateGroupBy: (payload) => dispatch(Creators.updatePrometheusGroupBy(payload)),
|
|
115
|
+
updatePrometheusMetricQueryField: (field, option) =>
|
|
116
|
+
dispatch(Creators.updatePrometheusMetricQueryField({ field, value: option && option.value })),
|
|
117
|
+
updateFilterQueryType: (value: PrometheusQueryType) => {
|
|
118
|
+
dispatch(Creators.updatePrometheusMetricQueryField({ field: 'queryType', value }));
|
|
119
|
+
dispatch(Creators.editTemplateCancel()); // clear template editing
|
|
120
|
+
dispatch(Creators.selectTemplate({ name: null })); // deselect template
|
|
121
|
+
dispatch(Creators.editInlineTemplate({ value: '' })); // clear inline template
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const prometheusMetricValidationSelector = createSelector(editingMetricSelector, validateMetric);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Validates Prometheus specific fields on the edit metric modal
|
|
130
|
+
*/
|
|
131
|
+
function validateMetric(editingMetric: ICanaryMetricConfig): ICanaryMetricValidationErrors {
|
|
132
|
+
const errors: ICanaryMetricValidationErrors = {
|
|
133
|
+
metricName: null,
|
|
134
|
+
};
|
|
135
|
+
return [validatePrometheusMetricName].reduce(
|
|
136
|
+
(reducedErrors, validator) => validator(reducedErrors, editingMetric),
|
|
137
|
+
errors,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Validates that the user has supplied a Prometheus metric.
|
|
143
|
+
*/
|
|
144
|
+
function validatePrometheusMetricName(
|
|
145
|
+
errors: ICanaryMetricValidationErrors,
|
|
146
|
+
editingMetric: ICanaryMetricConfig,
|
|
147
|
+
): ICanaryMetricValidationErrors {
|
|
148
|
+
const nextErrors = { ...errors };
|
|
149
|
+
const metricName = get(editingMetric, 'query.metricName', '');
|
|
150
|
+
if (!metricName) {
|
|
151
|
+
nextErrors.metricName = { message: 'The Prometheus metric is required.' };
|
|
152
|
+
return nextErrors;
|
|
153
|
+
}
|
|
154
|
+
return nextErrors;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default connect(mapStateToProps, mapDispatchToProps)(PrometheusMetricConfigurer);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { shallow } from 'enzyme';
|
|
2
|
+
import { DisableableReactSelect } from 'kayenta/layout/disableable';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { noop } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { IPrometheusMetricTypeSelectorProps, PrometheusMetricTypeSelector } from './metricTypeSelector';
|
|
8
|
+
|
|
9
|
+
describe('<PrometheusMetricTypeSelector />', () => {
|
|
10
|
+
let wrapper: any;
|
|
11
|
+
const defaultProps: IPrometheusMetricTypeSelectorProps = {
|
|
12
|
+
accountOptions: [
|
|
13
|
+
{
|
|
14
|
+
label: 'my-first-prometheus-account',
|
|
15
|
+
value: 'my-first-prometheus-account',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: 'my-second-prometheus-account',
|
|
19
|
+
value: 'my-second-prometheus-account',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
load: noop,
|
|
23
|
+
loading: false,
|
|
24
|
+
metricOptions: [],
|
|
25
|
+
onChange: noop,
|
|
26
|
+
value: '',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
it('renders a typeahead select to search for metrics', () => {
|
|
30
|
+
wrapper = shallow(<PrometheusMetricTypeSelector {...defaultProps} />);
|
|
31
|
+
expect(wrapper.find(DisableableReactSelect).length).toEqual(1);
|
|
32
|
+
expect(wrapper.find(DisableableReactSelect).props().placeholder).toEqual(
|
|
33
|
+
'Enter at least three characters to search.',
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('displays which account will populate the search when >1 account is configured, defaulting to the first account alphabetically', () => {
|
|
38
|
+
wrapper = shallow(<PrometheusMetricTypeSelector {...defaultProps} />);
|
|
39
|
+
expect(wrapper.find('.prometheus-metric-type-selector-account-hint span').at(0).text()).toEqual(
|
|
40
|
+
'Metric search is currently populating from my-first-prometheus-account.',
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('allows the user to switch which account populates the search when >1 account is configured', () => {
|
|
45
|
+
wrapper = shallow(<PrometheusMetricTypeSelector {...defaultProps} />);
|
|
46
|
+
expect(wrapper.find('.btn').text()).toEqual('Switch Account');
|
|
47
|
+
wrapper.find('.btn').simulate('click');
|
|
48
|
+
expect(wrapper.state('showAccountDropdown')).toEqual(true);
|
|
49
|
+
expect(wrapper.find(DisableableReactSelect).length).toEqual(2);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('does not display account selection hint when there is only one account configured', () => {
|
|
53
|
+
wrapper = shallow(
|
|
54
|
+
<PrometheusMetricTypeSelector
|
|
55
|
+
{...defaultProps}
|
|
56
|
+
accountOptions={[{ label: 'my-only-prometheus-account', value: 'my-only-prometheus-account' }]}
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
expect(wrapper.find('.prometheus-metric-type-selector-account-hint').length).toEqual(0);
|
|
60
|
+
expect(wrapper.find('.btn').length).toEqual(0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as Creators from 'kayenta/actions/creators';
|
|
2
|
+
import { KayentaAccountType } from 'kayenta/domain';
|
|
3
|
+
import { DISABLE_EDIT_CONFIG, DisableableReactSelect } from 'kayenta/layout/disableable';
|
|
4
|
+
import { ICanaryState } from 'kayenta/reducers';
|
|
5
|
+
import { AsyncRequestState } from 'kayenta/reducers/asyncRequest';
|
|
6
|
+
import { chain, get } from 'lodash';
|
|
7
|
+
import * as React from 'react';
|
|
8
|
+
import { connect } from 'react-redux';
|
|
9
|
+
import { Option } from 'react-select';
|
|
10
|
+
import { Dispatch } from 'redux';
|
|
11
|
+
import { createSelector } from 'reselect';
|
|
12
|
+
|
|
13
|
+
import { IPrometheusMetricDescriptor } from './domain/IPrometheusMetricDescriptor';
|
|
14
|
+
|
|
15
|
+
import './metricTypeSelector.less';
|
|
16
|
+
|
|
17
|
+
interface IPrometheusMetricTypeSelectorDispatchProps {
|
|
18
|
+
load: (filter: string, metricsAccountName: string) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface IPrometheusMetricTypeSelectorStateProps {
|
|
22
|
+
accountOptions: Array<Option<string>>;
|
|
23
|
+
loading: boolean;
|
|
24
|
+
metricOptions: Array<Option<string>>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface IPrometheusMetricTypeSelectorOwnProps {
|
|
28
|
+
onChange: (option: Option<string>) => void;
|
|
29
|
+
value: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface IPrometheusMetricTypeSelectorState {
|
|
33
|
+
selectedAccount: string;
|
|
34
|
+
showAccountDropdown: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type IPrometheusMetricTypeSelectorProps = IPrometheusMetricTypeSelectorDispatchProps &
|
|
38
|
+
IPrometheusMetricTypeSelectorStateProps &
|
|
39
|
+
IPrometheusMetricTypeSelectorOwnProps;
|
|
40
|
+
|
|
41
|
+
export class PrometheusMetricTypeSelector extends React.Component<
|
|
42
|
+
IPrometheusMetricTypeSelectorProps,
|
|
43
|
+
IPrometheusMetricTypeSelectorState
|
|
44
|
+
> {
|
|
45
|
+
public constructor(props: IPrometheusMetricTypeSelectorProps) {
|
|
46
|
+
super(props);
|
|
47
|
+
this.state = {
|
|
48
|
+
showAccountDropdown: false,
|
|
49
|
+
selectedAccount: get(props, ['accountOptions', 0, 'value']),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public render() {
|
|
54
|
+
const { accountOptions, load, loading, metricOptions, onChange, value } = this.props;
|
|
55
|
+
|
|
56
|
+
const metricOptionsWithSelected =
|
|
57
|
+
value && metricOptions.every((o) => o.value !== value)
|
|
58
|
+
? metricOptions.concat({ label: value, value })
|
|
59
|
+
: metricOptions;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<DisableableReactSelect
|
|
64
|
+
isLoading={loading}
|
|
65
|
+
options={metricOptionsWithSelected}
|
|
66
|
+
onChange={onChange}
|
|
67
|
+
value={value}
|
|
68
|
+
placeholder={'Enter at least three characters to search.'}
|
|
69
|
+
onInputChange={(input) => {
|
|
70
|
+
load(input, this.state.selectedAccount);
|
|
71
|
+
return input;
|
|
72
|
+
}}
|
|
73
|
+
disabledStateKeys={[DISABLE_EDIT_CONFIG]}
|
|
74
|
+
/>
|
|
75
|
+
{accountOptions.length > 1 && (
|
|
76
|
+
<div className="prometheus-metric-type-selector-account-hint">
|
|
77
|
+
<span>Metric search is currently populating from {this.state.selectedAccount}.</span>
|
|
78
|
+
<span className="btn btn-link" onClick={this.showAccountDropdown}>
|
|
79
|
+
{!this.state.showAccountDropdown && 'Switch Account'}
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
{this.state.showAccountDropdown && (
|
|
84
|
+
<DisableableReactSelect
|
|
85
|
+
clearable={false}
|
|
86
|
+
options={accountOptions}
|
|
87
|
+
onChange={this.selectAccount}
|
|
88
|
+
value={this.state.selectedAccount}
|
|
89
|
+
disabledStateKeys={[DISABLE_EDIT_CONFIG]}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private showAccountDropdown = (): void => {
|
|
97
|
+
this.setState({
|
|
98
|
+
showAccountDropdown: true,
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
private selectAccount = (option: Option<string>): void => {
|
|
103
|
+
this.setState({
|
|
104
|
+
selectedAccount: option.value,
|
|
105
|
+
showAccountDropdown: false,
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const accountOptionsSelector = createSelector(
|
|
111
|
+
(state: ICanaryState) => state.data.kayentaAccounts.data,
|
|
112
|
+
(accounts): Array<Option<string>> => {
|
|
113
|
+
return chain(accounts)
|
|
114
|
+
.filter((a) => a.supportedTypes.includes(KayentaAccountType.MetricsStore) && a.type === 'prometheus')
|
|
115
|
+
.sortBy((a) => a.name)
|
|
116
|
+
.map((a) => ({ label: a.name, value: a.name }))
|
|
117
|
+
.value();
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const metricOptionsSelector = createSelector(
|
|
122
|
+
(state: ICanaryState) => state.data.metricsServiceMetadata.data,
|
|
123
|
+
(descriptors: IPrometheusMetricDescriptor[]): Array<Option<string>> =>
|
|
124
|
+
descriptors.map((d) => ({ label: d.name, value: d.name })),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const mapStateToProps = (state: ICanaryState, ownProps: IPrometheusMetricTypeSelectorOwnProps) => {
|
|
128
|
+
return {
|
|
129
|
+
accountOptions: accountOptionsSelector(state),
|
|
130
|
+
metricOptions: metricOptionsSelector(state),
|
|
131
|
+
loading: state.data.metricsServiceMetadata.load === AsyncRequestState.Requesting,
|
|
132
|
+
...ownProps,
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const mapDispatchToProps = (dispatch: Dispatch<ICanaryState>) => {
|
|
137
|
+
return {
|
|
138
|
+
load: (filter: string, metricsAccountName: string) => {
|
|
139
|
+
dispatch(Creators.updatePrometheusMetricDescriptorFilter({ filter, metricsAccountName }));
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export default connect(mapStateToProps, mapDispatchToProps)(PrometheusMetricTypeSelector);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ICanaryMetricConfig } from '../../domain';
|
|
2
|
+
import { PrometheusQueryType } from './domain/IPrometheusCanaryMetricSetQueryConfig';
|
|
3
|
+
import { appendPromQLPrefix, getPrometheusQueryType, isQueryPromQL, removePromQLPrefix } from './queryTypeSelectors';
|
|
4
|
+
|
|
5
|
+
describe('Prometheus query type selectors', () => {
|
|
6
|
+
describe('isQueryPromQL', () => {
|
|
7
|
+
it('identifies PromQL queries by prefix', () => {
|
|
8
|
+
expect(isQueryPromQL('PromQL:count(prometheus_target_interval_length_seconds)')).toEqual(true);
|
|
9
|
+
expect(isQueryPromQL('metadata.user_labels."app"="${location}"')).toEqual(false);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
describe('removePromQLPrefix', () => {
|
|
13
|
+
it('removes PromQL prefix from query', () => {
|
|
14
|
+
expect(removePromQLPrefix('PromQL:count(prometheus_target_interval_length_seconds)')).toEqual(
|
|
15
|
+
'count(prometheus_target_interval_length_seconds)',
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
it('does not modify queries not prefixed by the PromQL prefix', () => {
|
|
19
|
+
expect(removePromQLPrefix('Not a PromQL template')).toEqual('Not a PromQL template');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('appendPromQLPrefix', () => {
|
|
23
|
+
it('appends the PromQL prefix to the input', () => {
|
|
24
|
+
expect(appendPromQLPrefix('count(prometheus_target_interval_length_seconds)')).toEqual(
|
|
25
|
+
'PromQL:count(prometheus_target_interval_length_seconds)',
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('getPrometheusQueryType', () => {
|
|
30
|
+
let editingMetric: ICanaryMetricConfig;
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
editingMetric = {
|
|
33
|
+
id: '#0',
|
|
34
|
+
name: 'my metric',
|
|
35
|
+
groups: ['group-1'],
|
|
36
|
+
analysisConfigurations: null,
|
|
37
|
+
scopeName: 'default',
|
|
38
|
+
query: {
|
|
39
|
+
customFilterTemplate: 'myDefaultTemplate',
|
|
40
|
+
queryType: PrometheusQueryType.DEFAULT,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
it('reads query type from metric state if set', () => {
|
|
45
|
+
expect(getPrometheusQueryType(editingMetric)).toEqual(PrometheusQueryType.DEFAULT);
|
|
46
|
+
editingMetric.query.queryType = PrometheusQueryType.PROMQL;
|
|
47
|
+
expect(getPrometheusQueryType(editingMetric)).toEqual(PrometheusQueryType.PROMQL);
|
|
48
|
+
});
|
|
49
|
+
it('infers query type from custom inline template if query type not set on state', () => {
|
|
50
|
+
editingMetric.query.queryType = null;
|
|
51
|
+
expect(getPrometheusQueryType(editingMetric)).toEqual(PrometheusQueryType.DEFAULT);
|
|
52
|
+
editingMetric.query.customInlineTemplate = 'PromQL:count(prometheus_target_interval_length_seconds)';
|
|
53
|
+
expect(getPrometheusQueryType(editingMetric)).toEqual(PrometheusQueryType.PROMQL);
|
|
54
|
+
});
|
|
55
|
+
it('defaults to PrometheusQueryType.DEFAULT if query type not set on state and no inline template added', () => {
|
|
56
|
+
editingMetric.query.queryType = null;
|
|
57
|
+
editingMetric.query.customInlineTemplate = null;
|
|
58
|
+
expect(getPrometheusQueryType(editingMetric)).toEqual(PrometheusQueryType.DEFAULT);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ICanaryMetricConfig } from '../../domain';
|
|
2
|
+
import { PrometheusQueryType } from './domain/IPrometheusCanaryMetricSetQueryConfig';
|
|
3
|
+
import { ITemplateTransformFunctions } from '../../selectors/filterTemplatesSelectors';
|
|
4
|
+
|
|
5
|
+
const PROMQL_PREFIX = 'PromQL:';
|
|
6
|
+
|
|
7
|
+
export function isQueryPromQL(template: string): boolean {
|
|
8
|
+
return template != null && template.startsWith(PROMQL_PREFIX);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function removePromQLPrefix(template: string): string {
|
|
12
|
+
if (isQueryPromQL(template)) {
|
|
13
|
+
return template.substring(PROMQL_PREFIX.length);
|
|
14
|
+
}
|
|
15
|
+
return template;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function appendPromQLPrefix(template: string): string {
|
|
19
|
+
return `${PROMQL_PREFIX}${template}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const getPrometheusQueryType = (editingMetric: ICanaryMetricConfig) => {
|
|
23
|
+
if (editingMetric.query.queryType) {
|
|
24
|
+
return editingMetric.query.queryType;
|
|
25
|
+
}
|
|
26
|
+
const inlineTemplate = editingMetric.query.customInlineTemplate;
|
|
27
|
+
if (isQueryPromQL(inlineTemplate)) {
|
|
28
|
+
return PrometheusQueryType.PROMQL;
|
|
29
|
+
}
|
|
30
|
+
return PrometheusQueryType.DEFAULT;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const prometheusQueryTypeToTransformFunction: { [queryType: string]: ITemplateTransformFunctions } = {
|
|
34
|
+
[PrometheusQueryType.PROMQL]: {
|
|
35
|
+
fromValue: removePromQLPrefix,
|
|
36
|
+
toValue: appendPromQLPrefix,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import SignalFxMetricConfigurer, { queryFinder } from './metricConfigurer';
|
|
2
|
+
import metricStoreConfigStore from '../metricStoreConfig.service';
|
|
3
|
+
|
|
4
|
+
metricStoreConfigStore.register({
|
|
5
|
+
name: 'signalfx',
|
|
6
|
+
metricConfigurer: SignalFxMetricConfigurer,
|
|
7
|
+
queryFinder,
|
|
8
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import autoBindMethods from 'class-autobind-decorator';
|
|
2
|
+
import * as Creators from 'kayenta/actions/creators';
|
|
3
|
+
import { ICanaryMetricConfig } from 'kayenta/domain';
|
|
4
|
+
import { DISABLE_EDIT_CONFIG, DisableableInput } from 'kayenta/layout/disableable';
|
|
5
|
+
import FormRow from 'kayenta/layout/formRow';
|
|
6
|
+
import { ICanaryState } from 'kayenta/reducers';
|
|
7
|
+
import { get } from 'lodash';
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import { connect } from 'react-redux';
|
|
10
|
+
import { Action } from 'redux';
|
|
11
|
+
import { createSelector } from 'reselect';
|
|
12
|
+
|
|
13
|
+
import { ICanaryMetricValidationErrors, MetricValidatorFunction } from '../../edit/editMetricValidation';
|
|
14
|
+
import KeyValueList, { IKeyValuePair, IUpdateKeyValueListPayload } from '../../layout/keyValueList';
|
|
15
|
+
import { editingMetricSelector } from '../../selectors';
|
|
16
|
+
|
|
17
|
+
import './metricConfigurer.less';
|
|
18
|
+
|
|
19
|
+
interface ISignalFxMetricConfigurerStateProps {
|
|
20
|
+
editingMetric: ICanaryMetricConfig;
|
|
21
|
+
validationErrors: ICanaryMetricValidationErrors;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ISignalFxMetricConfigurerDispatchProps {
|
|
25
|
+
updateMetricName: (name: string) => void;
|
|
26
|
+
updateAggregationMethod: (method: string) => void;
|
|
27
|
+
updateQueryPairs: (payload: IUpdateKeyValueListPayload) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type SignalFxMetricConfigurerProps = ISignalFxMetricConfigurerStateProps & ISignalFxMetricConfigurerDispatchProps;
|
|
31
|
+
|
|
32
|
+
export const queryFinder = (metric: ICanaryMetricConfig) => get(metric, 'query.metricName', '');
|
|
33
|
+
const getSignalFxMetric = queryFinder;
|
|
34
|
+
const getQueryPairs = (metric: ICanaryMetricConfig) => get(metric, 'query.queryPairs', []) as IKeyValuePair[];
|
|
35
|
+
const getAggregationMethod = (metric: ICanaryMetricConfig) => get(metric, 'query.aggregationMethod', '');
|
|
36
|
+
|
|
37
|
+
@autoBindMethods
|
|
38
|
+
class SignalFxMetricConfigurer extends React.Component<SignalFxMetricConfigurerProps> {
|
|
39
|
+
public onMetricNameChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
40
|
+
this.props.updateMetricName(e.target.value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public onAggregationMethodChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
44
|
+
this.props.updateAggregationMethod(e.target.value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public render() {
|
|
48
|
+
const { editingMetric, updateQueryPairs, validationErrors } = this.props;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<section>
|
|
52
|
+
<FormRow label="SignalFx Metric" error={get(validationErrors, 'signalFxMetric.message', null)} inputOnly={true}>
|
|
53
|
+
<DisableableInput
|
|
54
|
+
type="text"
|
|
55
|
+
value={getSignalFxMetric(editingMetric)}
|
|
56
|
+
onChange={this.onMetricNameChange}
|
|
57
|
+
disabledStateKeys={[DISABLE_EDIT_CONFIG]}
|
|
58
|
+
/>
|
|
59
|
+
</FormRow>
|
|
60
|
+
<FormRow
|
|
61
|
+
label="Aggregation Method"
|
|
62
|
+
inputOnly={true}
|
|
63
|
+
helpId="canary.config.signalFx.aggregationMethod"
|
|
64
|
+
error={get(validationErrors, 'aggregationMethod.message', null)}
|
|
65
|
+
>
|
|
66
|
+
<DisableableInput
|
|
67
|
+
type="text"
|
|
68
|
+
value={getAggregationMethod(editingMetric)}
|
|
69
|
+
onChange={this.onAggregationMethodChange}
|
|
70
|
+
disabledStateKeys={[DISABLE_EDIT_CONFIG]}
|
|
71
|
+
/>
|
|
72
|
+
</FormRow>
|
|
73
|
+
<FormRow
|
|
74
|
+
label="Query Pairs"
|
|
75
|
+
helpId="canary.config.signalFx.queryPairs"
|
|
76
|
+
error={get(validationErrors, 'queryPairs.message', null)}
|
|
77
|
+
>
|
|
78
|
+
<KeyValueList
|
|
79
|
+
className="signalfx-query-pairs"
|
|
80
|
+
list={getQueryPairs(editingMetric)}
|
|
81
|
+
actionCreator={updateQueryPairs}
|
|
82
|
+
/>
|
|
83
|
+
</FormRow>
|
|
84
|
+
</section>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Validates SignalFx specific fields on the edit metric modal
|
|
91
|
+
*/
|
|
92
|
+
export function validateMetric(editingMetric: ICanaryMetricConfig): ICanaryMetricValidationErrors {
|
|
93
|
+
const errors: ICanaryMetricValidationErrors = {};
|
|
94
|
+
|
|
95
|
+
const validators: MetricValidatorFunction[] = [...getSignalFxValidators(editingMetric)];
|
|
96
|
+
|
|
97
|
+
return validators.reduce((reducedErrors, validator) => validator(reducedErrors, editingMetric), errors);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* returns the list of validators for the SignalFx edit metric form.
|
|
102
|
+
*/
|
|
103
|
+
export function getSignalFxValidators(editingMetric: ICanaryMetricConfig): MetricValidatorFunction[] {
|
|
104
|
+
if (!editingMetric || !editingMetric.query) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return [validateSignalFxMetricName, validateAggregationMethod, validateQueryPairs];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validates that the user has supplied a SignalFx metric.
|
|
112
|
+
*/
|
|
113
|
+
function validateSignalFxMetricName(
|
|
114
|
+
errors: ICanaryMetricValidationErrors,
|
|
115
|
+
editingMetric: ICanaryMetricConfig,
|
|
116
|
+
): ICanaryMetricValidationErrors {
|
|
117
|
+
const nextErrors = { ...errors };
|
|
118
|
+
|
|
119
|
+
const signalFxMetric = getSignalFxMetric(editingMetric);
|
|
120
|
+
|
|
121
|
+
if (!signalFxMetric) {
|
|
122
|
+
nextErrors.signalFxMetric = { message: 'The SignalFx metric is required.' };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return nextErrors;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Validates that the user has supplied an aggregation method.
|
|
130
|
+
*/
|
|
131
|
+
function validateAggregationMethod(
|
|
132
|
+
errors: ICanaryMetricValidationErrors,
|
|
133
|
+
editingMetric: ICanaryMetricConfig,
|
|
134
|
+
): ICanaryMetricValidationErrors {
|
|
135
|
+
const nextErrors = { ...errors };
|
|
136
|
+
|
|
137
|
+
const aggregationMethod = getAggregationMethod(editingMetric);
|
|
138
|
+
|
|
139
|
+
if (!aggregationMethod) {
|
|
140
|
+
nextErrors.aggregationMethod = { message: 'The SignalFx SignalFlow stream aggregation method is required.' };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return nextErrors;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Validates that if the user has supplied query pairs that all key value combos contain values.
|
|
148
|
+
*/
|
|
149
|
+
function validateQueryPairs(
|
|
150
|
+
errors: ICanaryMetricValidationErrors,
|
|
151
|
+
editingMetric: ICanaryMetricConfig,
|
|
152
|
+
): ICanaryMetricValidationErrors {
|
|
153
|
+
const nextErrors = { ...errors };
|
|
154
|
+
|
|
155
|
+
const queryPairs: IKeyValuePair[] = getQueryPairs(editingMetric);
|
|
156
|
+
|
|
157
|
+
queryPairs.forEach((qp) => {
|
|
158
|
+
if (!qp.key || !qp.value) {
|
|
159
|
+
nextErrors.queryPairs = { message: 'All query pairs must contain a non-blank key and value.' };
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return nextErrors;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const sfxEditingMetricValidationErrorsSelector = createSelector(editingMetricSelector, validateMetric);
|
|
167
|
+
|
|
168
|
+
function mapStateToProps(state: ICanaryState): ISignalFxMetricConfigurerStateProps {
|
|
169
|
+
return {
|
|
170
|
+
editingMetric: state.selectedConfig.editingMetric,
|
|
171
|
+
validationErrors: sfxEditingMetricValidationErrorsSelector(state),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function mapDispatchToProps(dispatch: (action: Action & any) => void): ISignalFxMetricConfigurerDispatchProps {
|
|
176
|
+
return {
|
|
177
|
+
updateMetricName: (metricName: string): void => {
|
|
178
|
+
dispatch(Creators.updateSignalFxMetricName({ metricName }));
|
|
179
|
+
},
|
|
180
|
+
updateAggregationMethod: (aggregationMethod: string): void => {
|
|
181
|
+
dispatch(Creators.updateSignalFxAggregationMethod({ aggregationMethod }));
|
|
182
|
+
},
|
|
183
|
+
updateQueryPairs: (payload) => dispatch(Creators.updateSignalFxQueryPairs(payload)),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export default connect(mapStateToProps, mapDispatchToProps)(SignalFxMetricConfigurer);
|