@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.
Files changed (289) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintrc.js +1 -0
  3. package/.huskyrc +5 -0
  4. package/.lintstagedrc.json +4 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc.js +1 -0
  7. package/LICENSE.txt +203 -0
  8. package/README.md +81 -0
  9. package/__mocks__/styleMock.js +1 -0
  10. package/__mocks__/version.json +4 -0
  11. package/babel.config.js +3 -0
  12. package/build.gradle +67 -0
  13. package/build_scripts/checkLicenses.js +79 -0
  14. package/gradle.properties +0 -0
  15. package/jest.config.js +204 -0
  16. package/jest.setup.js +5 -0
  17. package/package.json +166 -0
  18. package/rollup-plugin-angularjs-template-loader.js +82 -0
  19. package/rollup.config.js +30 -0
  20. package/src/index.ts +2 -0
  21. package/src/kayenta/actions/creators.ts +163 -0
  22. package/src/kayenta/actions/index.ts +98 -0
  23. package/src/kayenta/canary.dataSource.bridge.ts +53 -0
  24. package/src/kayenta/canary.dataSource.stub.ts +64 -0
  25. package/src/kayenta/canary.help.ts +136 -0
  26. package/src/kayenta/canary.less +168 -0
  27. package/src/kayenta/canary.settings.ts +26 -0
  28. package/src/kayenta/canary.tsx +67 -0
  29. package/src/kayenta/components/canaryScore.component.less +77 -0
  30. package/src/kayenta/components/canaryScore.component.ts +12 -0
  31. package/src/kayenta/components/canaryScore.tsx +63 -0
  32. package/src/kayenta/components/canaryScores.component.ts +20 -0
  33. package/src/kayenta/components/canaryScores.less +22 -0
  34. package/src/kayenta/components/canaryScores.tsx +163 -0
  35. package/src/kayenta/components/loadStates.tsx +52 -0
  36. package/src/kayenta/domain/ICanaryConfig.ts +57 -0
  37. package/src/kayenta/domain/ICanaryConfigSummary.ts +7 -0
  38. package/src/kayenta/domain/ICanaryConfigUpdateResponse.ts +3 -0
  39. package/src/kayenta/domain/ICanaryExecutionStatusResult.ts +72 -0
  40. package/src/kayenta/domain/ICanaryJudgeResult.ts +51 -0
  41. package/src/kayenta/domain/ICanaryJudgeResultSummary.ts +5 -0
  42. package/src/kayenta/domain/ICanaryScoreThresholds.ts +4 -0
  43. package/src/kayenta/domain/IJudge.ts +5 -0
  44. package/src/kayenta/domain/IKayentaAccount.ts +14 -0
  45. package/src/kayenta/domain/IKayentaStageConfig.ts +58 -0
  46. package/src/kayenta/domain/IMetricSetPair.ts +17 -0
  47. package/src/kayenta/domain/IMetricsServiceMetadata.ts +2 -0
  48. package/src/kayenta/domain/ISetupCanaryStage.ts +11 -0
  49. package/src/kayenta/domain/MetricClassificationLabel.ts +8 -0
  50. package/src/kayenta/domain/ScoreClassificationLabel.ts +7 -0
  51. package/src/kayenta/domain/index.ts +15 -0
  52. package/src/kayenta/edit/changeMetricGroupModal.tsx +107 -0
  53. package/src/kayenta/edit/configDetail.tsx +31 -0
  54. package/src/kayenta/edit/configDetailActionButtons.tsx +24 -0
  55. package/src/kayenta/edit/configDetailHeader.tsx +104 -0
  56. package/src/kayenta/edit/configDetailLoadStates.tsx +36 -0
  57. package/src/kayenta/edit/configDetailLoader.tsx +60 -0
  58. package/src/kayenta/edit/configJson.less +42 -0
  59. package/src/kayenta/edit/configJsonModal.tsx +158 -0
  60. package/src/kayenta/edit/configList.less +7 -0
  61. package/src/kayenta/edit/configList.tsx +57 -0
  62. package/src/kayenta/edit/copyConfigButton.tsx +34 -0
  63. package/src/kayenta/edit/createConfigButton.tsx +34 -0
  64. package/src/kayenta/edit/deleteModal.tsx +87 -0
  65. package/src/kayenta/edit/edit.tsx +24 -0
  66. package/src/kayenta/edit/editMetricEffectSizes.tsx +186 -0
  67. package/src/kayenta/edit/editMetricModal.less +9 -0
  68. package/src/kayenta/edit/editMetricModal.spec.tsx +129 -0
  69. package/src/kayenta/edit/editMetricModal.tsx +294 -0
  70. package/src/kayenta/edit/editMetricValidation.spec.ts +63 -0
  71. package/src/kayenta/edit/editMetricValidation.ts +50 -0
  72. package/src/kayenta/edit/filterTemplateSelector.less +15 -0
  73. package/src/kayenta/edit/filterTemplateSelector.spec.tsx +106 -0
  74. package/src/kayenta/edit/filterTemplateSelector.tsx +194 -0
  75. package/src/kayenta/edit/filterTemplatesValidation.spec.ts +108 -0
  76. package/src/kayenta/edit/filterTemplatesValidation.ts +95 -0
  77. package/src/kayenta/edit/footer.less +30 -0
  78. package/src/kayenta/edit/footer.tsx +12 -0
  79. package/src/kayenta/edit/groupName.tsx +80 -0
  80. package/src/kayenta/edit/groupTabs.tsx +106 -0
  81. package/src/kayenta/edit/groupWeight.tsx +80 -0
  82. package/src/kayenta/edit/groupWeights.tsx +38 -0
  83. package/src/kayenta/edit/inlineTemplateEditor.spec.tsx +42 -0
  84. package/src/kayenta/edit/inlineTemplateEditor.tsx +61 -0
  85. package/src/kayenta/edit/judgeSelect.tsx +82 -0
  86. package/src/kayenta/edit/metricConfigurerDelegator.tsx +30 -0
  87. package/src/kayenta/edit/metricList.less +21 -0
  88. package/src/kayenta/edit/metricList.tsx +215 -0
  89. package/src/kayenta/edit/metricStoreSelector.tsx +66 -0
  90. package/src/kayenta/edit/nameAndDescription.tsx +90 -0
  91. package/src/kayenta/edit/openConfigJsonModalButton.tsx +33 -0
  92. package/src/kayenta/edit/openDeleteModalButton.tsx +50 -0
  93. package/src/kayenta/edit/ownedBy.tsx +34 -0
  94. package/src/kayenta/edit/save.tsx +19 -0
  95. package/src/kayenta/edit/saveConfigButton.tsx +65 -0
  96. package/src/kayenta/edit/saveConfigError.tsx +59 -0
  97. package/src/kayenta/edit/scoring.tsx +35 -0
  98. package/src/kayenta/edit/selectConfig.tsx +10 -0
  99. package/src/kayenta/edit/validationErrors.tsx +39 -0
  100. package/src/kayenta/index.ts +6 -0
  101. package/src/kayenta/layout/addNewButton.tsx +20 -0
  102. package/src/kayenta/layout/centeredDetail.tsx +13 -0
  103. package/src/kayenta/layout/deleteButton.tsx +11 -0
  104. package/src/kayenta/layout/disableable.tsx +87 -0
  105. package/src/kayenta/layout/formList.tsx +26 -0
  106. package/src/kayenta/layout/formRow.tsx +36 -0
  107. package/src/kayenta/layout/formattedDate.tsx +14 -0
  108. package/src/kayenta/layout/index.ts +2 -0
  109. package/src/kayenta/layout/keyValueList.less +20 -0
  110. package/src/kayenta/layout/keyValueList.tsx +114 -0
  111. package/src/kayenta/layout/list.less +9 -0
  112. package/src/kayenta/layout/list.spec.tsx +83 -0
  113. package/src/kayenta/layout/list.tsx +73 -0
  114. package/src/kayenta/layout/listDetail.tsx +33 -0
  115. package/src/kayenta/layout/radioChoice.tsx +29 -0
  116. package/src/kayenta/layout/styleguide.tsx +16 -0
  117. package/src/kayenta/layout/table/index.ts +5 -0
  118. package/src/kayenta/layout/table/nativeTable.tsx +51 -0
  119. package/src/kayenta/layout/table/nativeTableHeader.tsx +26 -0
  120. package/src/kayenta/layout/table/table.tsx +56 -0
  121. package/src/kayenta/layout/table/tableColumn.ts +7 -0
  122. package/src/kayenta/layout/table/tableHeader.tsx +23 -0
  123. package/src/kayenta/layout/tabs.tsx +26 -0
  124. package/src/kayenta/layout/titledSection.less +16 -0
  125. package/src/kayenta/layout/titledSection.tsx +20 -0
  126. package/src/kayenta/layout/titledSubsection.less +11 -0
  127. package/src/kayenta/layout/titledSubsection.tsx +22 -0
  128. package/src/kayenta/manualAnalysis/ManualAnalysisModal.tsx +716 -0
  129. package/src/kayenta/metricStore/atlas/atlasMetricConfigurer.tsx +130 -0
  130. package/src/kayenta/metricStore/atlas/index.ts +8 -0
  131. package/src/kayenta/metricStore/datadog/domain/IDatadogMetricDescriptor.ts +5 -0
  132. package/src/kayenta/metricStore/datadog/index.ts +9 -0
  133. package/src/kayenta/metricStore/datadog/metricConfigurer.tsx +90 -0
  134. package/src/kayenta/metricStore/datadog/metricTypeSelector.spec.tsx +59 -0
  135. package/src/kayenta/metricStore/datadog/metricTypeSelector.tsx +73 -0
  136. package/src/kayenta/metricStore/graphite/domain/IGraphiteMetricDescriptor.ts +5 -0
  137. package/src/kayenta/metricStore/graphite/index.ts +8 -0
  138. package/src/kayenta/metricStore/graphite/metricConfigurer.tsx +54 -0
  139. package/src/kayenta/metricStore/graphite/metricTypeSelector.tsx +80 -0
  140. package/src/kayenta/metricStore/graphite/typeahead.less +3 -0
  141. package/src/kayenta/metricStore/index.ts +8 -0
  142. package/src/kayenta/metricStore/metricStoreConfig.service.ts +12 -0
  143. package/src/kayenta/metricStore/newrelic/domain/INewRelicMetricDescriptor.ts +5 -0
  144. package/src/kayenta/metricStore/newrelic/index.ts +8 -0
  145. package/src/kayenta/metricStore/newrelic/metricConfigurer.tsx +58 -0
  146. package/src/kayenta/metricStore/prometheus/domain/IPrometheusCanaryMetricSetQueryConfig.ts +14 -0
  147. package/src/kayenta/metricStore/prometheus/domain/IPrometheusMetricDescriptor.ts +5 -0
  148. package/src/kayenta/metricStore/prometheus/index.ts +12 -0
  149. package/src/kayenta/metricStore/prometheus/metricConfigurer.tsx +157 -0
  150. package/src/kayenta/metricStore/prometheus/metricTypeSelector.less +5 -0
  151. package/src/kayenta/metricStore/prometheus/metricTypeSelector.spec.tsx +62 -0
  152. package/src/kayenta/metricStore/prometheus/metricTypeSelector.tsx +144 -0
  153. package/src/kayenta/metricStore/prometheus/queryTypeSelectors.spec.ts +61 -0
  154. package/src/kayenta/metricStore/prometheus/queryTypeSelectors.ts +38 -0
  155. package/src/kayenta/metricStore/signalfx/domain/ISignalFxCanaryMetricSetQueryConfig.ts +7 -0
  156. package/src/kayenta/metricStore/signalfx/index.ts +8 -0
  157. package/src/kayenta/metricStore/signalfx/metricConfigurer.less +10 -0
  158. package/src/kayenta/metricStore/signalfx/metricConfigurer.tsx +187 -0
  159. package/src/kayenta/metricStore/stackdriver/domain/IStackdriverCanaryMetricSetQueryConfig.ts +9 -0
  160. package/src/kayenta/metricStore/stackdriver/domain/IStackdriverMetricDescriptor.ts +17 -0
  161. package/src/kayenta/metricStore/stackdriver/index.ts +12 -0
  162. package/src/kayenta/metricStore/stackdriver/metricConfigurer.tsx +144 -0
  163. package/src/kayenta/metricStore/stackdriver/metricTypeSelector.spec.tsx +92 -0
  164. package/src/kayenta/metricStore/stackdriver/metricTypeSelector.tsx +113 -0
  165. package/src/kayenta/middleware/actionInterceptor.ts +29 -0
  166. package/src/kayenta/middleware/asyncDispatch.ts +37 -0
  167. package/src/kayenta/middleware/epics.ts +211 -0
  168. package/src/kayenta/middleware/index.ts +3 -0
  169. package/src/kayenta/navigation/canary.states.stub.ts +28 -0
  170. package/src/kayenta/navigation/canary.states.ts +182 -0
  171. package/src/kayenta/reducers/app.ts +56 -0
  172. package/src/kayenta/reducers/asyncRequest.ts +5 -0
  173. package/src/kayenta/reducers/data.ts +169 -0
  174. package/src/kayenta/reducers/editingTemplate.ts +54 -0
  175. package/src/kayenta/reducers/group.ts +82 -0
  176. package/src/kayenta/reducers/index.ts +245 -0
  177. package/src/kayenta/reducers/prometheusMetricConfig.spec.ts +33 -0
  178. package/src/kayenta/reducers/prometheusMetricConfig.ts +56 -0
  179. package/src/kayenta/reducers/selectedConfig.spec.ts +190 -0
  180. package/src/kayenta/reducers/selectedConfig.ts +566 -0
  181. package/src/kayenta/reducers/selectedRun.ts +101 -0
  182. package/src/kayenta/reducers/signalFxMetricConfig.ts +36 -0
  183. package/src/kayenta/reducers/stackdriverMetricConfig.spec.ts +33 -0
  184. package/src/kayenta/reducers/stackdriverMetricConfig.ts +41 -0
  185. package/src/kayenta/reducers/templates.spec.ts +192 -0
  186. package/src/kayenta/reducers/validators.ts +118 -0
  187. package/src/kayenta/report/detail/allMetricResultsHeader.tsx +32 -0
  188. package/src/kayenta/report/detail/clickableHeader.tsx +21 -0
  189. package/src/kayenta/report/detail/colors.ts +47 -0
  190. package/src/kayenta/report/detail/detail.less +16 -0
  191. package/src/kayenta/report/detail/detail.tsx +48 -0
  192. package/src/kayenta/report/detail/detailLoader.tsx +55 -0
  193. package/src/kayenta/report/detail/graph/graph.tsx +37 -0
  194. package/src/kayenta/report/detail/graph/metricSetPairGraph.service.ts +35 -0
  195. package/src/kayenta/report/detail/graph/semiotic/boxplot.less +45 -0
  196. package/src/kayenta/report/detail/graph/semiotic/boxplot.tsx +283 -0
  197. package/src/kayenta/report/detail/graph/semiotic/chartHeader.tsx +19 -0
  198. package/src/kayenta/report/detail/graph/semiotic/chartLegend.less +26 -0
  199. package/src/kayenta/report/detail/graph/semiotic/chartLegend.tsx +42 -0
  200. package/src/kayenta/report/detail/graph/semiotic/circleIcon.tsx +16 -0
  201. package/src/kayenta/report/detail/graph/semiotic/config.less +5 -0
  202. package/src/kayenta/report/detail/graph/semiotic/config.ts +38 -0
  203. package/src/kayenta/report/detail/graph/semiotic/customAxisTickLabel.tsx +17 -0
  204. package/src/kayenta/report/detail/graph/semiotic/declarations/labella.d.ts +16 -0
  205. package/src/kayenta/report/detail/graph/semiotic/declarations/react-container-dimensions.d.ts +3 -0
  206. package/src/kayenta/report/detail/graph/semiotic/declarations/semiotic.d.ts +160 -0
  207. package/src/kayenta/report/detail/graph/semiotic/differenceArea.less +17 -0
  208. package/src/kayenta/report/detail/graph/semiotic/differenceArea.tsx +186 -0
  209. package/src/kayenta/report/detail/graph/semiotic/histogram.less +22 -0
  210. package/src/kayenta/report/detail/graph/semiotic/histogram.tsx +251 -0
  211. package/src/kayenta/report/detail/graph/semiotic/index.tsx +19 -0
  212. package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.less +5 -0
  213. package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.tsx +10 -0
  214. package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.less +6 -0
  215. package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.tsx +58 -0
  216. package/src/kayenta/report/detail/graph/semiotic/semiotic.service.ts +32 -0
  217. package/src/kayenta/report/detail/graph/semiotic/semioticGraph.less +53 -0
  218. package/src/kayenta/report/detail/graph/semiotic/semioticGraph.tsx +49 -0
  219. package/src/kayenta/report/detail/graph/semiotic/timeSeries.less +42 -0
  220. package/src/kayenta/report/detail/graph/semiotic/timeSeries.tsx +473 -0
  221. package/src/kayenta/report/detail/graph/semiotic/tooltip.tsx +55 -0
  222. package/src/kayenta/report/detail/graph/semiotic/utils.ts +90 -0
  223. package/src/kayenta/report/detail/graphTypeSelector.less +4 -0
  224. package/src/kayenta/report/detail/graphTypeSelector.tsx +50 -0
  225. package/src/kayenta/report/detail/groupScores.tsx +68 -0
  226. package/src/kayenta/report/detail/header.less +70 -0
  227. package/src/kayenta/report/detail/header.tsx +39 -0
  228. package/src/kayenta/report/detail/headerArrow.tsx +13 -0
  229. package/src/kayenta/report/detail/loadStates.tsx +31 -0
  230. package/src/kayenta/report/detail/metricResultActions.less +29 -0
  231. package/src/kayenta/report/detail/metricResultActions.tsx +87 -0
  232. package/src/kayenta/report/detail/metricResultClassification.tsx +22 -0
  233. package/src/kayenta/report/detail/metricResultDetail.tsx +20 -0
  234. package/src/kayenta/report/detail/metricResultDetailLayout.tsx +19 -0
  235. package/src/kayenta/report/detail/metricResultDeviation.tsx +25 -0
  236. package/src/kayenta/report/detail/metricResultStats.less +9 -0
  237. package/src/kayenta/report/detail/metricResultStats.tsx +120 -0
  238. package/src/kayenta/report/detail/metricResults.less +12 -0
  239. package/src/kayenta/report/detail/metricResults.tsx +52 -0
  240. package/src/kayenta/report/detail/metricResultsClassificationFilters.tsx +65 -0
  241. package/src/kayenta/report/detail/metricResultsColumns.tsx +27 -0
  242. package/src/kayenta/report/detail/metricResultsList.less +44 -0
  243. package/src/kayenta/report/detail/metricResultsList.tsx +120 -0
  244. package/src/kayenta/report/detail/metricSetPairLoadStates.tsx +22 -0
  245. package/src/kayenta/report/detail/multipleResultsTable.tsx +81 -0
  246. package/src/kayenta/report/detail/reportException.tsx +57 -0
  247. package/src/kayenta/report/detail/reportExplanation.less +12 -0
  248. package/src/kayenta/report/detail/reportExplanation.tsx +32 -0
  249. package/src/kayenta/report/detail/reportMetadata.tsx +167 -0
  250. package/src/kayenta/report/detail/reportScores.less +47 -0
  251. package/src/kayenta/report/detail/reportScores.tsx +80 -0
  252. package/src/kayenta/report/detail/score.tsx +33 -0
  253. package/src/kayenta/report/detail/sourceLinks.tsx +69 -0
  254. package/src/kayenta/report/list/configLink.tsx +32 -0
  255. package/src/kayenta/report/list/executionList.less +7 -0
  256. package/src/kayenta/report/list/loadStates.tsx +32 -0
  257. package/src/kayenta/report/list/pipelineLink.tsx +15 -0
  258. package/src/kayenta/report/list/reportLink.tsx +33 -0
  259. package/src/kayenta/report/list/table.tsx +309 -0
  260. package/src/kayenta/report/report.tsx +11 -0
  261. package/src/kayenta/selectors/filterTemplatesSelectors.ts +87 -0
  262. package/src/kayenta/selectors/index.ts +62 -0
  263. package/src/kayenta/service/canaryConfig.service.ts +122 -0
  264. package/src/kayenta/service/canaryRun.service.ts +60 -0
  265. package/src/kayenta/service/delegateFactory.ts +24 -0
  266. package/src/kayenta/service/metricsServiceMetadata.service.ts +9 -0
  267. package/src/kayenta/stages/kayentaStage/AnalysisType.spec.tsx +47 -0
  268. package/src/kayenta/stages/kayentaStage/AnalysisType.tsx +49 -0
  269. package/src/kayenta/stages/kayentaStage/CanaryExecutionLabel.tsx +26 -0
  270. package/src/kayenta/stages/kayentaStage/analysisType.component.ts +12 -0
  271. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.component.ts +12 -0
  272. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.less +5 -0
  273. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.tsx +136 -0
  274. package/src/kayenta/stages/kayentaStage/forAnalysisType.component.ts +45 -0
  275. package/src/kayenta/stages/kayentaStage/kayentaStage.controller.ts +789 -0
  276. package/src/kayenta/stages/kayentaStage/kayentaStage.html +528 -0
  277. package/src/kayenta/stages/kayentaStage/kayentaStage.less +5 -0
  278. package/src/kayenta/stages/kayentaStage/kayentaStage.transformer.ts +179 -0
  279. package/src/kayenta/stages/kayentaStage/kayentaStage.ts +221 -0
  280. package/src/kayenta/stages/kayentaStage/kayentaStageConfigSection.component.ts +21 -0
  281. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.controller.ts +88 -0
  282. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.html +114 -0
  283. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.less +6 -0
  284. package/src/kayenta/stages/kayentaStage/stageTypes.ts +5 -0
  285. package/src/kayenta/utils/duration.spec.ts +69 -0
  286. package/src/kayenta/utils/duration.ts +48 -0
  287. package/src/lazy.ts +29 -0
  288. package/src/stub.ts +60 -0
  289. package/tsconfig.json +11 -0
@@ -0,0 +1,158 @@
1
+ import 'brace/ext/searchbox';
2
+ import 'brace/mode/json';
3
+ import * as Creators from 'kayenta/actions/creators';
4
+ import { CanarySettings } from 'kayenta/canary.settings';
5
+ import Styleguide from 'kayenta/layout/styleguide';
6
+ import { Tab, Tabs } from 'kayenta/layout/tabs';
7
+ import { ICanaryState } from 'kayenta/reducers';
8
+ import { mapStateToConfig } from 'kayenta/service/canaryConfig.service';
9
+ import { get, omit } from 'lodash';
10
+ import * as React from 'react';
11
+ import { Modal } from 'react-bootstrap';
12
+ import { connect } from 'react-redux';
13
+ import { Action } from 'redux';
14
+
15
+ import { DiffView, IJsonDiff, JsonEditor, JsonUtils } from '@spinnaker/core';
16
+
17
+ import './configJson.less';
18
+
19
+ interface IConfigJsonDispatchProps {
20
+ closeModal: () => void;
21
+ setConfigJson: (json: string) => void;
22
+ updateConfig: (event: any) => void;
23
+ setTabState: (state: ConfigJsonModalTabState) => () => void;
24
+ }
25
+
26
+ interface IConfigJsonStateProps {
27
+ show: boolean;
28
+ disabled: boolean;
29
+ configJson: string;
30
+ id: string;
31
+ deserializationError: string;
32
+ tabState: ConfigJsonModalTabState;
33
+ diff: IJsonDiff;
34
+ }
35
+
36
+ export enum ConfigJsonModalTabState {
37
+ Edit,
38
+ Diff,
39
+ }
40
+
41
+ /*
42
+ * Modal for viewing canary config JSON.
43
+ */
44
+ function ConfigJsonModal({
45
+ show,
46
+ disabled,
47
+ configJson,
48
+ id,
49
+ deserializationError,
50
+ closeModal,
51
+ setConfigJson,
52
+ updateConfig,
53
+ setTabState,
54
+ tabState,
55
+ diff,
56
+ }: IConfigJsonDispatchProps & IConfigJsonStateProps) {
57
+ return (
58
+ <Modal show={show} onHide={(): void => null} bsSize="large">
59
+ <Styleguide>
60
+ <Modal.Header>
61
+ <Modal.Title>JSON</Modal.Title>
62
+ </Modal.Header>
63
+ <Modal.Body>
64
+ <section>
65
+ <Tabs>
66
+ <Tab selected={tabState === ConfigJsonModalTabState.Edit}>
67
+ <a onClick={setTabState(ConfigJsonModalTabState.Edit)}>Edit</a>
68
+ </Tab>
69
+ <Tab selected={tabState === ConfigJsonModalTabState.Diff}>
70
+ <a onClick={setTabState(ConfigJsonModalTabState.Diff)}>Diff</a>
71
+ </Tab>
72
+ </Tabs>
73
+ </section>
74
+ <section className="kayenta-config-json">
75
+ {tabState === ConfigJsonModalTabState.Edit && (
76
+ <JsonEditor value={configJson} debounceChangePeriod={500} onChange={setConfigJson} readOnly={disabled} />
77
+ )}
78
+ {tabState === ConfigJsonModalTabState.Diff && !deserializationError && (
79
+ <div className="modal-show-history">
80
+ <div className="show-history">
81
+ <DiffView diff={diff} />
82
+ </div>
83
+ </div>
84
+ )}
85
+ {!!deserializationError && (
86
+ <div className="horizontal row center">
87
+ <span className="error-message">Error: {deserializationError}</span>
88
+ </div>
89
+ )}
90
+ </section>
91
+ </Modal.Body>
92
+ <Modal.Footer>
93
+ <ul className="list-inline pull-right">
94
+ <li>
95
+ <button className="passive" onClick={closeModal}>
96
+ Close
97
+ </button>
98
+ </li>
99
+ <li>
100
+ <button
101
+ className="primary"
102
+ data-id={id}
103
+ data-serialized={configJson}
104
+ onClick={updateConfig}
105
+ disabled={!!deserializationError || disabled}
106
+ >
107
+ Update
108
+ </button>
109
+ </li>
110
+ </ul>
111
+ </Modal.Footer>
112
+ </Styleguide>
113
+ </Modal>
114
+ );
115
+ }
116
+
117
+ function mapDispatchToProps(dispatch: (action: Action & any) => void): IConfigJsonDispatchProps {
118
+ return {
119
+ closeModal: () => dispatch(Creators.closeConfigJsonModal()),
120
+ setTabState: (state: ConfigJsonModalTabState) => () => dispatch(Creators.setConfigJsonModalTabState({ state })),
121
+ setConfigJson: (json: string) => dispatch(Creators.setConfigJson({ json })),
122
+ updateConfig: (event: any) => {
123
+ dispatch(
124
+ Creators.selectConfig({
125
+ config: {
126
+ id: event.target.dataset.id,
127
+ ...JSON.parse(event.target.dataset.serialized),
128
+ },
129
+ }),
130
+ );
131
+ },
132
+ };
133
+ }
134
+
135
+ function mapStateToProps(state: ICanaryState): IConfigJsonStateProps {
136
+ const id: string = get(state, 'selectedConfig.config.id');
137
+ const persistedConfig = JsonUtils.makeSortedStringFromObject(
138
+ omit(state.data.configs.find((c) => c.id === id) || {}, 'id'),
139
+ );
140
+
141
+ const configJson =
142
+ state.selectedConfig.json.configJson ||
143
+ JsonUtils.makeSortedStringFromObject(omit(mapStateToConfig(state) || {}, 'id'));
144
+
145
+ const disabled = state.app.disableConfigEdit || CanarySettings.disableConfigEdit;
146
+
147
+ return {
148
+ configJson,
149
+ id,
150
+ disabled,
151
+ show: state.app.configJsonModalOpen,
152
+ deserializationError: state.selectedConfig.json.error,
153
+ tabState: state.app.configJsonModalTabState,
154
+ diff: state.selectedConfig.json.error ? null : JsonUtils.diff(persistedConfig, configJson, true),
155
+ };
156
+ }
157
+
158
+ export default connect(mapStateToProps, mapDispatchToProps)(ConfigJsonModal);
@@ -0,0 +1,7 @@
1
+ .config-list > .tabs-vertical > li {
2
+ padding: 0;
3
+
4
+ & > a {
5
+ padding: 18px 12px;
6
+ }
7
+ }
@@ -0,0 +1,57 @@
1
+ import { UISref } from '@uirouter/react';
2
+ import { ICanaryConfigSummary } from 'kayenta/domain/ICanaryConfigSummary';
3
+ import FormattedDate from 'kayenta/layout/formattedDate';
4
+ import { ICanaryState } from 'kayenta/reducers';
5
+ import { sortBy } from 'lodash';
6
+ import * as React from 'react';
7
+ import { connect } from 'react-redux';
8
+
9
+ import CreateConfigButton from './createConfigButton';
10
+ import { OwnedBy } from './ownedBy';
11
+
12
+ import './configList.less';
13
+
14
+ interface IConfigListStateProps {
15
+ configs: ICanaryConfigSummary[];
16
+ selectedConfigId: string;
17
+ application: string;
18
+ }
19
+
20
+ /*
21
+ * Shows a list of available configurations the user can select for editing.
22
+ */
23
+ function ConfigList({ configs, selectedConfigId, application }: IConfigListStateProps) {
24
+ return (
25
+ <section className="config-list">
26
+ <ul className="tabs-vertical list-unstyled " style={{ wordBreak: 'break-all' }}>
27
+ {configs.map((config) => (
28
+ <li key={config.id} className={config.id === selectedConfigId ? 'selected' : ''}>
29
+ <UISref to=".configDetail" params={{ id: config.id, new: false, copy: false }}>
30
+ <a>
31
+ <div className="heading-4 color-text-primary">{config.name}</div>
32
+ <div className="body-small color-text-caption caption" style={{ marginTop: '5px', marginBottom: '0' }}>
33
+ Edited: <FormattedDate dateIso={config.updatedTimestampIso} />
34
+ <br />
35
+ <OwnedBy owningApplications={config.applications} currentApplication={application} />
36
+ </div>
37
+ </a>
38
+ </UISref>
39
+ </li>
40
+ ))}
41
+ </ul>
42
+ <CreateConfigButton />
43
+ </section>
44
+ );
45
+ }
46
+
47
+ function mapStateToProps(state: ICanaryState): IConfigListStateProps {
48
+ const selectedConfigId = state.selectedConfig.config ? state.selectedConfig.config.id : null;
49
+ const application = state.data.application.name;
50
+ return {
51
+ selectedConfigId,
52
+ configs: sortBy(state.data.configSummaries, 'name'),
53
+ application,
54
+ };
55
+ }
56
+
57
+ export default connect(mapStateToProps)(ConfigList);
@@ -0,0 +1,34 @@
1
+ import { UISref } from '@uirouter/react';
2
+ import { CanarySettings } from 'kayenta/canary.settings';
3
+ import { get } from 'lodash';
4
+ import * as React from 'react';
5
+ import { connect } from 'react-redux';
6
+
7
+ import { ICanaryState } from '../reducers/index';
8
+
9
+ interface ICopyConfigButtonStateProps {
10
+ disabled: boolean;
11
+ }
12
+
13
+ /*
14
+ * Button for copying a canary config.
15
+ */
16
+ function CopyConfigButton({ disabled }: ICopyConfigButtonStateProps) {
17
+ return (
18
+ <UISref to="^.configDetail" params={{ copy: true }}>
19
+ <button className="passive" disabled={disabled}>
20
+ <i className="fa fa-copy" />
21
+ <span>Copy</span>
22
+ </button>
23
+ </UISref>
24
+ );
25
+ }
26
+
27
+ function mapStateToProps(state: ICanaryState) {
28
+ return {
29
+ disabled:
30
+ get(state.selectedConfig, 'config.isNew') || state.app.disableConfigEdit || CanarySettings.disableConfigEdit,
31
+ };
32
+ }
33
+
34
+ export default connect(mapStateToProps)(CopyConfigButton);
@@ -0,0 +1,34 @@
1
+ import { UISref } from '@uirouter/react';
2
+ import { CanarySettings } from 'kayenta/canary.settings';
3
+ import * as React from 'react';
4
+ import { connect } from 'react-redux';
5
+
6
+ import { UUIDGenerator } from '@spinnaker/core';
7
+
8
+ import { ICanaryState } from '../reducers/index';
9
+
10
+ interface ICreateConfigButtonStateProps {
11
+ disabled: boolean;
12
+ }
13
+
14
+ /*
15
+ * Button for creating a new canary config.
16
+ */
17
+ function CreateConfigButton({ disabled }: ICreateConfigButtonStateProps) {
18
+ return (
19
+ <UISref to=".configDetail" params={{ id: UUIDGenerator.generateUuid(), new: true }}>
20
+ <button className="zombie text-left form-control" disabled={disabled}>
21
+ <i className="fa fa-plus-circle" />
22
+ <span>Add configuration</span>
23
+ </button>
24
+ </UISref>
25
+ );
26
+ }
27
+
28
+ function mapStateToProps(state: ICanaryState): ICreateConfigButtonStateProps {
29
+ return {
30
+ disabled: (state.selectedConfig.config && state.selectedConfig.config.isNew) || CanarySettings.disableConfigEdit,
31
+ };
32
+ }
33
+
34
+ export default connect(mapStateToProps)(CreateConfigButton);
@@ -0,0 +1,87 @@
1
+ import classNames from 'classnames';
2
+ import * as Creators from 'kayenta/actions/creators';
3
+ import * as React from 'react';
4
+ import { Modal } from 'react-bootstrap';
5
+ import { connect } from 'react-redux';
6
+ import { Action } from 'redux';
7
+
8
+ import Styleguide from '../layout/styleguide';
9
+ import { AsyncRequestState } from '../reducers/asyncRequest';
10
+ import { ICanaryState } from '../reducers/index';
11
+
12
+ interface IDeleteModalDispatchProps {
13
+ closeDeleteConfigModal: () => void;
14
+ deleteConfig: () => void;
15
+ }
16
+
17
+ interface IDeleteModalStateProps {
18
+ show: boolean;
19
+ selectedConfigName: string;
20
+ deleteConfigState: AsyncRequestState;
21
+ deleteConfigErrorMessage: string;
22
+ }
23
+
24
+ /*
25
+ * Modal to confirm canary config deletion.
26
+ */
27
+ function DeleteConfigModal({
28
+ show,
29
+ selectedConfigName,
30
+ deleteConfigState,
31
+ deleteConfigErrorMessage,
32
+ closeDeleteConfigModal,
33
+ deleteConfig,
34
+ }: IDeleteModalDispatchProps & IDeleteModalStateProps) {
35
+ return (
36
+ <Modal show={show} onHide={null}>
37
+ <Styleguide>
38
+ <Modal.Header>
39
+ <Modal.Title>Really delete {selectedConfigName}?</Modal.Title>
40
+ </Modal.Header>
41
+ {deleteConfigState === AsyncRequestState.Failed && (
42
+ <Modal.Body>
43
+ {/*TODO: create generic error message component */}
44
+ <span className={classNames('alert', 'alert-danger')}>{buildErrorMessage(deleteConfigErrorMessage)}</span>
45
+ </Modal.Body>
46
+ )}
47
+ <Modal.Footer>
48
+ <ul className="list-inline pull-right">
49
+ <li>
50
+ <button className="passive" onClick={closeDeleteConfigModal}>
51
+ Cancel
52
+ </button>
53
+ </li>
54
+ <li>
55
+ <button className="primary" onClick={deleteConfig}>
56
+ Delete
57
+ </button>
58
+ </li>
59
+ </ul>
60
+ </Modal.Footer>
61
+ </Styleguide>
62
+ </Modal>
63
+ );
64
+ }
65
+
66
+ function buildErrorMessage(deleteConfigErrorMessage: string): string {
67
+ const message = 'The was an error deleting your config';
68
+ return deleteConfigErrorMessage ? message + `: ${deleteConfigErrorMessage}.` : message + '.';
69
+ }
70
+
71
+ function mapDispatchToProps(dispatch: (action: Action & any) => void): IDeleteModalDispatchProps {
72
+ return {
73
+ closeDeleteConfigModal: () => dispatch(Creators.closeDeleteConfigModal()),
74
+ deleteConfig: () => dispatch(Creators.deleteConfig()),
75
+ };
76
+ }
77
+
78
+ function mapStateToProps(state: ICanaryState): IDeleteModalStateProps {
79
+ return {
80
+ show: state.app.deleteConfigModalOpen,
81
+ selectedConfigName: state.selectedConfig.config ? state.selectedConfig.config.name : null,
82
+ deleteConfigState: state.selectedConfig.destroy.state,
83
+ deleteConfigErrorMessage: state.selectedConfig.destroy.error,
84
+ };
85
+ }
86
+
87
+ export default connect(mapStateToProps, mapDispatchToProps)(DeleteConfigModal);
@@ -0,0 +1,24 @@
1
+ import { UIView } from '@uirouter/react';
2
+ import * as React from 'react';
3
+
4
+ import ConfigList from './configList';
5
+ import Footer from './footer';
6
+ import ListDetail from '../layout/listDetail';
7
+
8
+ /*
9
+ * Component for editing canary configurations for an application.
10
+ */
11
+ export default function CanaryConfigEdit() {
12
+ const List = <ConfigList />;
13
+ // TODO: need to break these down by groups
14
+ const noWrap = { wrap: false };
15
+ const Detail = <UIView {...noWrap} name="detail" />;
16
+ return (
17
+ <div className="vertical flex-1">
18
+ <ListDetail list={List} detail={Detail} className="flex-1" detailClass="vertical flex-1" />
19
+ <Footer>
20
+ <UIView {...noWrap} name="footer" />
21
+ </Footer>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,186 @@
1
+ import * as Creators from 'kayenta/actions/creators';
2
+ import { CanarySettings } from 'kayenta/canary.settings';
3
+ import { ICanaryMetricEffectSizeConfig } from 'kayenta/domain';
4
+ import FormRow from 'kayenta/layout/formRow';
5
+ import RadioChoice from 'kayenta/layout/radioChoice';
6
+ import { ICanaryState } from 'kayenta/reducers';
7
+ import * as React from 'react';
8
+ import { connect } from 'react-redux';
9
+
10
+ import { HelpField, robotToHuman } from '@spinnaker/core';
11
+
12
+ import { DISABLE_EDIT_CONFIG, DisableableInput } from '../layout/disableable';
13
+
14
+ interface IEditMetricEffectSizesProps {
15
+ metricId: string;
16
+ direction: string;
17
+ disabled: boolean;
18
+ effectSizes: ICanaryMetricEffectSizeConfig;
19
+ }
20
+
21
+ interface IEditMetricEffectSizesDispatchProps {
22
+ updateEffectSize: (id: string, value: ICanaryMetricEffectSizeConfig) => void;
23
+ }
24
+
25
+ export function EditMetricEffectSizes({
26
+ effectSizes = {},
27
+ updateEffectSize,
28
+ direction,
29
+ metricId,
30
+ disabled,
31
+ }: IEditMetricEffectSizesProps & IEditMetricEffectSizesDispatchProps) {
32
+ const [showEffectSizes, setShowEffectSizes] = React.useState<boolean>(!!Object.keys(effectSizes).length);
33
+ // only allow editing of measurement type if it was set to "cles" programmatically
34
+ const [allowEditMeasurementType] = React.useState<boolean>(effectSizes.measure === 'cles');
35
+
36
+ const description = (
37
+ <p>
38
+ Effect sizes should be used when your canary data produces a lot of metrics that flag as "high", but where you
39
+ only want the test to fail if they are sufficiently larger or smaller than the control data.
40
+ </p>
41
+ );
42
+
43
+ if (!showEffectSizes && disabled) {
44
+ return null;
45
+ }
46
+
47
+ if (!showEffectSizes) {
48
+ return (
49
+ <FormRow label="Effect Sizes">
50
+ {description}
51
+ <button className="passive" onClick={() => setShowEffectSizes(true)}>
52
+ Configure effect sizes
53
+ </button>
54
+ </FormRow>
55
+ );
56
+ }
57
+
58
+ const isCles = effectSizes.measure === 'cles';
59
+ const defaultSize = isCles ? 0.5 : 1;
60
+
61
+ const {
62
+ allowedIncrease = defaultSize,
63
+ criticalIncrease = defaultSize,
64
+ allowedDecrease = defaultSize,
65
+ criticalDecrease = defaultSize,
66
+ measure = 'meanRatio',
67
+ } = effectSizes;
68
+
69
+ const updateSize = (field: keyof ICanaryMetricEffectSizeConfig, percent: string) => {
70
+ const value = isCles ? Number(percent) : fromPercent(field, percent);
71
+ const newEffectSizes = { ...effectSizes, [field]: value };
72
+ if (value === defaultSize) {
73
+ delete newEffectSizes[field];
74
+ }
75
+ updateEffectSize(metricId, newEffectSizes);
76
+ };
77
+
78
+ const updateMeasure = (event: React.ChangeEvent<HTMLInputElement>) => {
79
+ updateEffectSize(metricId, { ...effectSizes, measure: event.target.value });
80
+ };
81
+
82
+ const fromPercent = (field: keyof ICanaryMetricEffectSizeConfig, percent: string) => {
83
+ const size = Number(percent) / 100;
84
+ return field.endsWith('Increase') ? 1 + size : 1 - size;
85
+ };
86
+
87
+ const toPercent = (value: number, field: keyof ICanaryMetricEffectSizeConfig) => {
88
+ const normalized = field.endsWith('Increase') ? value - 1 : 1 - value;
89
+ return Math.round(normalized * 100);
90
+ };
91
+
92
+ const clesLabel = (
93
+ <>
94
+ CLES <HelpField id="canary.config.effectSize.cles" />
95
+ </>
96
+ );
97
+
98
+ const noEffectLabel = (effect: number) => {
99
+ if (effect === defaultSize) {
100
+ return <HelpField expand={true} content="(will not affect metric evaluation)" />;
101
+ }
102
+ return null;
103
+ };
104
+
105
+ const buildInput = (field: keyof ICanaryMetricEffectSizeConfig, value: number) => (
106
+ <div className="form-group flex-1">
107
+ <label>{robotToHuman(field)}</label>
108
+ <div className="row">
109
+ <div className="col-md-4">
110
+ <div className="input-group effect-size-wrapper">
111
+ <DisableableInput
112
+ className="form-control input-sm"
113
+ type="number"
114
+ min={0}
115
+ step={isCles ? 0.01 : 1}
116
+ onChange={(e) => updateSize(field, e.target.value)}
117
+ value={isCles ? value : toPercent(value, field)}
118
+ disabled={CanarySettings.disableConfigEdit}
119
+ disabledStateKeys={[DISABLE_EDIT_CONFIG]}
120
+ />
121
+ {!isCles && <div className="input-group-addon">%</div>}
122
+ </div>
123
+ </div>
124
+ </div>
125
+ {noEffectLabel(value)}
126
+ </div>
127
+ );
128
+
129
+ return (
130
+ <FormRow label="Effect Sizes">
131
+ {description}
132
+ <div className="vertical" style={{ marginTop: '5px' }}>
133
+ {allowEditMeasurementType && (
134
+ <div className="form-group vertical">
135
+ <label>Measurement Type</label>
136
+ <div>
137
+ <RadioChoice
138
+ value="meanRatio"
139
+ label="Mean (default)"
140
+ name="measure"
141
+ current={measure}
142
+ action={updateMeasure}
143
+ />
144
+ <RadioChoice value="cles" label={clesLabel} name="measure" current={measure} action={updateMeasure} />
145
+ </div>
146
+ </div>
147
+ )}
148
+ <label className="sp-margin-m-top">Allowed Effects</label>
149
+ <p>
150
+ If the canary surpasses the baseline by the supplied (non-zero) percentage, the <em>metric</em> will fail.
151
+ </p>
152
+ <div className="horizontal">
153
+ {direction !== 'increase' && buildInput('allowedDecrease', allowedDecrease)}
154
+ {direction !== 'decrease' && buildInput('allowedIncrease', allowedIncrease)}
155
+ </div>
156
+ <label className="sp-margin-m-top">Critical Effects</label>
157
+ <p>
158
+ If the canary surpasses the baseline by the supplied (non-zero) percentage, the <em>entire analysis</em> will
159
+ fail with a score of 0.
160
+ </p>
161
+ <div className="horizontal">
162
+ {direction !== 'increase' && buildInput('criticalDecrease', criticalDecrease)}
163
+ {direction !== 'decrease' && buildInput('criticalIncrease', criticalIncrease)}
164
+ </div>
165
+ </div>
166
+ </FormRow>
167
+ );
168
+ }
169
+
170
+ function mapStateToProps(state: ICanaryState): IEditMetricEffectSizesProps {
171
+ return {
172
+ metricId: state.selectedConfig.editingMetric.id,
173
+ direction: state.selectedConfig.editingMetric.analysisConfigurations?.canary?.direction ?? 'either',
174
+ disabled: state.app.disableConfigEdit || CanarySettings.disableConfigEdit,
175
+ effectSizes: state.selectedConfig.editingMetric.analysisConfigurations?.canary?.effectSize ?? {},
176
+ };
177
+ }
178
+
179
+ function mapDispatchToProps(dispatch: any): IEditMetricEffectSizesDispatchProps {
180
+ return {
181
+ updateEffectSize: (id: string, value: ICanaryMetricEffectSizeConfig) =>
182
+ dispatch(Creators.updateEffectSize({ id, value })),
183
+ };
184
+ }
185
+
186
+ export default connect(mapStateToProps, mapDispatchToProps)(EditMetricEffectSizes);
@@ -0,0 +1,9 @@
1
+ .kayenta-edit-metric-modal {
2
+ .modal-content {
3
+ // Prevents the contents of a ReactSelect field from being clipped by the bottom of the modal.
4
+ overflow: visible;
5
+ }
6
+ .effect-size-wrapper {
7
+ width: 100px;
8
+ }
9
+ }