@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,50 @@
1
+ import * as Creators from 'kayenta/actions/creators';
2
+ import { ICanaryState } from 'kayenta/reducers';
3
+ import * as React from 'react';
4
+ import { connect, Dispatch } from 'react-redux';
5
+
6
+ import { GraphType } from './graph/metricSetPairGraph.service';
7
+
8
+ import './graphTypeSelector.less';
9
+
10
+ interface IGraphTypeSelectorStateProps {
11
+ selected: GraphType;
12
+ }
13
+
14
+ interface IGraphTypeSelectorDispatchProps {
15
+ selectGraphType: (type: GraphType) => void;
16
+ }
17
+
18
+ const GraphTypeSelector = ({
19
+ selected,
20
+ selectGraphType,
21
+ }: IGraphTypeSelectorStateProps & IGraphTypeSelectorDispatchProps) => {
22
+ return (
23
+ <ul className="list-inline graph-type-selector">
24
+ <li>
25
+ <label className="label uppercase color-text-primary" style={{ paddingLeft: 0 }}>
26
+ Graph:
27
+ </label>
28
+ </li>
29
+ {Object.values(GraphType).map((type) => (
30
+ <li
31
+ style={selected === type ? { textDecoration: 'underline' } : null}
32
+ key={type}
33
+ onClick={() => selectGraphType(type)}
34
+ >
35
+ <a className="small clickable">{type}</a>
36
+ </li>
37
+ ))}
38
+ </ul>
39
+ );
40
+ };
41
+
42
+ const mapStateToProps = (state: ICanaryState): IGraphTypeSelectorStateProps => ({
43
+ selected: state.selectedRun.graphType,
44
+ });
45
+
46
+ const mapDispatchToProps = (dispatch: Dispatch<ICanaryState>): IGraphTypeSelectorDispatchProps => ({
47
+ selectGraphType: (type: GraphType) => dispatch(Creators.selectGraphType({ type })),
48
+ });
49
+
50
+ export default connect(mapStateToProps, mapDispatchToProps)(GraphTypeSelector);
@@ -0,0 +1,68 @@
1
+ import classNames from 'classnames';
2
+ import * as Creators from 'kayenta/actions/creators';
3
+ import { ICanaryJudgeGroupScore, ICanaryScoreThresholds, IGroupWeights } from 'kayenta/domain';
4
+ import { ICanaryState } from 'kayenta/reducers';
5
+ import { canaryExecutionRequestSelector, serializedGroupWeightsSelector } from 'kayenta/selectors';
6
+ import * as React from 'react';
7
+ import { connect, Dispatch } from 'react-redux';
8
+
9
+ import ClickableHeader from './clickableHeader';
10
+ import { mapGroupToColor } from './colors';
11
+
12
+ export interface IGroupScoresOwnProps {
13
+ groups: ICanaryJudgeGroupScore[];
14
+ className?: string;
15
+ }
16
+
17
+ interface IGroupScoresStateProps {
18
+ groupWeights: IGroupWeights;
19
+ scoreThresholds: ICanaryScoreThresholds;
20
+ selectedGroup: string;
21
+ }
22
+
23
+ interface IGroupScoresDispatchProps {
24
+ select: (event: any) => void;
25
+ }
26
+
27
+ /*
28
+ * Renders list of group scores.
29
+ * */
30
+ const GroupScores = ({
31
+ groups,
32
+ groupWeights,
33
+ scoreThresholds,
34
+ className,
35
+ select,
36
+ selectedGroup,
37
+ }: IGroupScoresOwnProps & IGroupScoresDispatchProps & IGroupScoresStateProps) => (
38
+ <section className={classNames('horizontal', className)}>
39
+ {groups.map((g) => (
40
+ <ClickableHeader
41
+ key={g.name}
42
+ style={{
43
+ width: `${groupWeights[g.name]}%`, // TODO: at some point (around 4%), the group name doesn't fit.
44
+ backgroundColor: mapGroupToColor(g, scoreThresholds),
45
+ }}
46
+ onClick={() => select(g.name)}
47
+ label={g.name}
48
+ className={classNames('report-score', { active: g.name === selectedGroup })}
49
+ />
50
+ ))}
51
+ </section>
52
+ );
53
+
54
+ const mapStateToProps = (state: ICanaryState): IGroupScoresStateProps => ({
55
+ selectedGroup: state.selectedRun.selectedGroup,
56
+ groupWeights: serializedGroupWeightsSelector(state),
57
+ scoreThresholds: canaryExecutionRequestSelector(state).thresholds,
58
+ });
59
+
60
+ const mapDispatchToProps = (
61
+ dispatch: Dispatch<ICanaryState>,
62
+ ownProps: IGroupScoresOwnProps,
63
+ ): IGroupScoresOwnProps & IGroupScoresDispatchProps => ({
64
+ select: (group: string) => dispatch(Creators.selectReportMetricGroup({ group })),
65
+ ...ownProps,
66
+ });
67
+
68
+ export default connect(mapStateToProps, mapDispatchToProps)(GroupScores);
@@ -0,0 +1,70 @@
1
+ .report-header {
2
+ margin-bottom: 10px;
3
+ align-items: center;
4
+
5
+ h1 {
6
+ flex: 1 0 200px;
7
+ align-items: center;
8
+ margin: 0;
9
+ padding: 0;
10
+ word-break: break-all;
11
+ }
12
+
13
+ .report-score-wrapper {
14
+ padding: 5px 20px 0 0;
15
+ margin-left: 15px;
16
+ border-right: 1px solid var(--color-titanium);
17
+ text-align: center;
18
+ flex: 0 0 200px;
19
+ }
20
+
21
+ .report-metadata {
22
+ flex: 0 1 auto;
23
+ display: flex;
24
+ overflow: hidden;
25
+ padding-left: 20px;
26
+ padding-top: 10px;
27
+ width: 100%;
28
+
29
+ .group {
30
+ flex: 1 1 200px;
31
+ margin-right: 20px;
32
+ overflow: hidden;
33
+
34
+ &.group-time {
35
+ flex: 0 0 150px;
36
+ }
37
+ &.group-threshold {
38
+ flex: 0 0 80px;
39
+ }
40
+ &.group-source {
41
+ flex: 0 0 64px;
42
+ margin-right: 0;
43
+ }
44
+ }
45
+
46
+ ul {
47
+ margin-bottom: 0;
48
+ }
49
+
50
+ .label {
51
+ padding-left: 0;
52
+ &.label-lg {
53
+ font-size: 85%;
54
+ }
55
+ }
56
+ }
57
+
58
+ .kayenta-scope {
59
+ width: 100%;
60
+ overflow: hidden;
61
+ white-space: nowrap;
62
+ text-overflow: ellipsis;
63
+ }
64
+ }
65
+
66
+ .popover {
67
+ .kayenta-scope {
68
+ word-break: break-all;
69
+ }
70
+ }
@@ -0,0 +1,39 @@
1
+ import { UISref } from '@uirouter/react';
2
+ import { ICanaryJudgeScore } from 'kayenta/domain/ICanaryJudgeResult';
3
+ import { ICanaryState } from 'kayenta/reducers';
4
+ import { judgeResultSelector, serializedCanaryConfigSelector } from 'kayenta/selectors';
5
+ import * as React from 'react';
6
+ import { connect } from 'react-redux';
7
+
8
+ import ReportMetadata from './reportMetadata';
9
+ import ReportScore from './score';
10
+
11
+ import './header.less';
12
+
13
+ export interface IReportHeaderStateProps {
14
+ id: string;
15
+ name: string;
16
+ score: ICanaryJudgeScore;
17
+ }
18
+
19
+ const ReportHeader = ({ id, name, score }: IReportHeaderStateProps) => (
20
+ <section className="horizontal report-header">
21
+ <div className="report-score-wrapper">
22
+ <ReportScore score={score} showClassification={true} />
23
+ <h1 className="heading-2 color-text-primary">
24
+ <UISref to="^.^.canaryConfig.configDetail" params={{ id }}>
25
+ <a className="clickable color-text-primary"> {name}</a>
26
+ </UISref>
27
+ </h1>
28
+ </div>
29
+ <ReportMetadata />
30
+ </section>
31
+ );
32
+
33
+ const mapStateToProps = (state: ICanaryState): IReportHeaderStateProps => ({
34
+ id: serializedCanaryConfigSelector(state).id,
35
+ name: serializedCanaryConfigSelector(state).name,
36
+ score: judgeResultSelector(state).score,
37
+ });
38
+
39
+ export default connect(mapStateToProps)(ReportHeader);
@@ -0,0 +1,13 @@
1
+ import classNames from 'classnames';
2
+ import * as React from 'react';
3
+
4
+ const ARROW_CLASS = 'arrow-down';
5
+
6
+ export interface IHeaderArrowProps {
7
+ arrowColor: string;
8
+ className?: string;
9
+ }
10
+
11
+ export default ({ arrowColor, className }: IHeaderArrowProps) => (
12
+ <div className={classNames(ARROW_CLASS, className)} style={{ borderTopColor: arrowColor }} />
13
+ );
@@ -0,0 +1,31 @@
1
+ import LoadStatesBuilder from 'kayenta/components/loadStates';
2
+ import CenteredDetail from 'kayenta/layout/centeredDetail';
3
+ import { ICanaryState } from 'kayenta/reducers';
4
+ import { AsyncRequestState } from 'kayenta/reducers/asyncRequest';
5
+ import * as React from 'react';
6
+ import { connect } from 'react-redux';
7
+
8
+ import ReportDetail from './detail';
9
+
10
+ interface IReportLoadStatesStateProps {
11
+ loadState: AsyncRequestState;
12
+ }
13
+
14
+ const ReportLoadStates = ({ loadState }: IReportLoadStatesStateProps) => {
15
+ const LoadStates = new LoadStatesBuilder()
16
+ .onFulfilled(<ReportDetail />)
17
+ .onFailed(
18
+ <CenteredDetail>
19
+ <h3 className="heading-3">Could not load canary report.</h3>
20
+ </CenteredDetail>,
21
+ )
22
+ .build();
23
+
24
+ return <LoadStates state={loadState} />;
25
+ };
26
+
27
+ const mapStateToProps = (state: ICanaryState) => ({
28
+ loadState: state.selectedRun.load,
29
+ });
30
+
31
+ export default connect(mapStateToProps)(ReportLoadStates);
@@ -0,0 +1,29 @@
1
+ .metric-result-actions {
2
+ .actions-layout {
3
+ display: flex;
4
+ flex-direction: row;
5
+ justify-content: flex-end;
6
+ margin: 12px 4px;
7
+ }
8
+ .action {
9
+ padding-left: 8px;
10
+ }
11
+ .copy-button-container {
12
+ display: flex;
13
+ flex-direction: row;
14
+ align-items: center;
15
+ position: relative;
16
+ .clipboard-btn {
17
+ width: 100%;
18
+ height: 100%;
19
+ opacity: 0;
20
+ z-index: 2;
21
+ position: absolute;
22
+ }
23
+ .copy-button {
24
+ .copy-icon {
25
+ top: -1px;
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,87 @@
1
+ import { CanarySettings } from 'kayenta/canary.settings';
2
+ import { ICanaryMetricConfig } from 'kayenta/domain/ICanaryConfig';
3
+ import { IMetricSetPair } from 'kayenta/domain/IMetricSetPair';
4
+ import metricStoreConfigStore from 'kayenta/metricStore/metricStoreConfig.service';
5
+ import { ICanaryState } from 'kayenta/reducers';
6
+ import { selectedMetricConfigSelector } from 'kayenta/selectors';
7
+ import * as React from 'react';
8
+ import { connect } from 'react-redux';
9
+
10
+ import { CopyToClipboard } from '@spinnaker/core';
11
+
12
+ import './metricResultActions.less';
13
+
14
+ export interface IMetricResultStatsStateProps {
15
+ metricConfig: ICanaryMetricConfig;
16
+ metricSetPair: IMetricSetPair;
17
+ }
18
+
19
+ const buildAtlasGraphUrl = (metricSetPair: IMetricSetPair) => {
20
+ const { attributes, scopes, values, tags } = metricSetPair;
21
+ const { atlasGraphBaseUrl } = CanarySettings;
22
+ let legendTags = '';
23
+ if (Object.keys(tags).length) {
24
+ legendTags = ` (${Object.keys(tags)
25
+ .map((tag) => `$${tag}`)
26
+ .join('|')})`;
27
+ }
28
+
29
+ // TODO: If the control and experiment have different baseURLs, generate two links instead of a combined one.
30
+ const backend = encodeURIComponent(attributes.control.baseURL);
31
+ const experimentQuery = encodeURIComponent(attributes.experiment.query);
32
+ const controlQuery = encodeURIComponent(attributes.control.query);
33
+ const query = `${experimentQuery},Canary${legendTags},:legend,:freeze,${controlQuery},Baseline${legendTags},:legend`;
34
+
35
+ const startTime = Math.min(scopes.control.startTimeMillis, scopes.experiment.startTimeMillis);
36
+ const controlEndTime = scopes.control.startTimeMillis + values.control.length * scopes.control.stepMillis;
37
+ const experimentEndTime = scopes.experiment.startTimeMillis + values.experiment.length * scopes.experiment.stepMillis;
38
+ const endTime = Math.max(controlEndTime, experimentEndTime);
39
+
40
+ return `${atlasGraphBaseUrl}?backend=${backend}&g.q=${query}&g.s=${startTime}&g.e=${endTime}&g.w=651&mode=png&axis=0`;
41
+ };
42
+
43
+ const MetricResultActions = ({ metricSetPair, metricConfig }: IMetricResultStatsStateProps) => {
44
+ const atlasURL = buildAtlasGraphUrl(metricSetPair);
45
+ const atlasQuery = metricStoreConfigStore.getDelegate(metricConfig.query.type).queryFinder(metricConfig);
46
+
47
+ // Mask CopyToClipboard component as a larger button, fire event when clicked
48
+ const copyToClipboard = (
49
+ <div className="copy-button-container">
50
+ <CopyToClipboard displayText={false} text={atlasQuery} toolTip={null} />
51
+ <button className="primary copy-button" key="copy-link">
52
+ <i className="glyphicon glyphicon-copy copy-icon" />
53
+ Copy metric query
54
+ </button>
55
+ </div>
56
+ );
57
+
58
+ const openAtlas = (
59
+ <a href={atlasURL} target="_blank">
60
+ <button className="primary">
61
+ <i className="fas fa-chart-line" />
62
+ Explore more data in Atlas
63
+ </button>
64
+ </a>
65
+ );
66
+
67
+ const actions = [copyToClipboard, openAtlas].map((action, i) => {
68
+ return (
69
+ <li className="action" key={i}>
70
+ {action}
71
+ </li>
72
+ );
73
+ });
74
+
75
+ return (
76
+ <div className="metric-result-actions">
77
+ <ul className="actions-layout list-inline">{actions}</ul>
78
+ </div>
79
+ );
80
+ };
81
+
82
+ const mapStateToProps = (state: ICanaryState): IMetricResultStatsStateProps => ({
83
+ metricConfig: selectedMetricConfigSelector(state),
84
+ metricSetPair: state.selectedRun.metricSetPair.pair,
85
+ });
86
+
87
+ export default connect(mapStateToProps)(MetricResultActions);
@@ -0,0 +1,22 @@
1
+ import classNames from 'classnames';
2
+ import { MetricClassificationLabel } from 'kayenta/domain/MetricClassificationLabel';
3
+ import * as React from 'react';
4
+
5
+ import { mapMetricClassificationToColor } from './colors';
6
+
7
+ interface IMetricResultClassificationProps {
8
+ classification: MetricClassificationLabel;
9
+ className?: string;
10
+ }
11
+
12
+ const buildStyle = (classification: MetricClassificationLabel) => ({
13
+ backgroundColor: mapMetricClassificationToColor(classification),
14
+ });
15
+
16
+ const TEXT_COLOR = 'var(--color-text-on-dark)';
17
+
18
+ export default ({ classification, className }: IMetricResultClassificationProps) => (
19
+ <div className={classNames('pill', 'metric-result-classification', className)} style={buildStyle(classification)}>
20
+ <span style={{ color: TEXT_COLOR }}>{classification}</span>
21
+ </div>
22
+ );
@@ -0,0 +1,20 @@
1
+ import { ICanaryAnalysisResult } from 'kayenta/domain/ICanaryJudgeResult';
2
+ import * as React from 'react';
3
+
4
+ import MetricSetPairLoadStates from './metricSetPairLoadStates';
5
+
6
+ export interface IMetricResultDetailProps {
7
+ result: ICanaryAnalysisResult;
8
+ }
9
+
10
+ /*
11
+ * Top-level component for the metric result detail - i.e., the right side of the
12
+ * screen that opens when you click on a metric result.
13
+ * */
14
+ export default ({ result }: IMetricResultDetailProps) => {
15
+ if (result) {
16
+ return <MetricSetPairLoadStates />;
17
+ } else {
18
+ return <h3 className="heading-3 text-center">Select a metric result.</h3>;
19
+ }
20
+ };
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+
3
+ import Graph from './graph/graph';
4
+ import GraphTypeSelector from './graphTypeSelector';
5
+ import MetricResultActions from './metricResultActions';
6
+ import MetricResultStats from './metricResultStats';
7
+
8
+ /*
9
+ * Responsible for layout of the metric result detail view after the metric set
10
+ * pair for the result has loaded successfully.
11
+ * */
12
+ export default () => (
13
+ <section className="vertical flex-1 sp-padding-xl-bottom" style={{ overflowY: 'auto' }}>
14
+ <GraphTypeSelector />
15
+ <Graph />
16
+ <MetricResultActions />
17
+ <MetricResultStats />
18
+ </section>
19
+ );
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+
3
+ interface IMetricResultDeviationProps {
4
+ className?: string;
5
+ ratio: number;
6
+ }
7
+
8
+ const formatDeviationAsPercentage = (ratio: number) => {
9
+ if (typeof ratio !== 'number') {
10
+ return 'N/A';
11
+ } else if (ratio === 1) {
12
+ return '0%';
13
+ } else if (ratio < 1) {
14
+ return ((ratio - 1) * 100).toFixed(1) + '%';
15
+ } else {
16
+ return '+' + ((ratio - 1) * 100).toFixed(1) + '%';
17
+ }
18
+ };
19
+
20
+ export default ({ ratio, className }: IMetricResultDeviationProps) =>
21
+ ratio && (
22
+ <span className={className} style={{ color: 'var(--color-text-caption' }}>
23
+ {formatDeviationAsPercentage(ratio)}
24
+ </span>
25
+ );
@@ -0,0 +1,9 @@
1
+ .metric-stats {
2
+ .label {
3
+ padding-left: 0;
4
+ }
5
+
6
+ p {
7
+ margin: 0;
8
+ }
9
+ }
@@ -0,0 +1,120 @@
1
+ import { ICanaryAnalysisResultsStats } from 'kayenta/domain';
2
+ import { ICanaryMetricConfig } from 'kayenta/domain/ICanaryConfig';
3
+ import { ICanaryExecutionStatusResult } from 'kayenta/domain/ICanaryExecutionStatusResult';
4
+ import { IMetricSetPair } from 'kayenta/domain/IMetricSetPair';
5
+ import FormattedDate from 'kayenta/layout/formattedDate';
6
+ import { ITableColumn, NativeTable } from 'kayenta/layout/table';
7
+ import { ICanaryState } from 'kayenta/reducers';
8
+ import { runSelector, selectedMetricConfigSelector } from 'kayenta/selectors';
9
+ import { round } from 'lodash';
10
+ import * as React from 'react';
11
+ import { connect } from 'react-redux';
12
+
13
+ import './metricResultStats.less';
14
+
15
+ export interface IMetricResultStatsStateProps {
16
+ metricConfig: ICanaryMetricConfig;
17
+ metricSetPair: IMetricSetPair;
18
+ run: ICanaryExecutionStatusResult;
19
+ service: string;
20
+ }
21
+
22
+ const getStats = (run: ICanaryExecutionStatusResult, id: string, target: string): ICanaryAnalysisResultsStats => {
23
+ const result = run.result.judgeResult.results.find((r) => r.id === id);
24
+ if (target === 'experiment') {
25
+ return result.experimentMetadata.stats;
26
+ } else if (target === 'control') {
27
+ return result.controlMetadata.stats;
28
+ } else {
29
+ return null;
30
+ }
31
+ };
32
+
33
+ interface IResultMetadataRow {
34
+ label: string;
35
+ getContent: () => JSX.Element;
36
+ }
37
+
38
+ const ResultMetadataRow = ({ row }: { row: IResultMetadataRow }) => {
39
+ if (!row.getContent()) {
40
+ return null;
41
+ }
42
+
43
+ return (
44
+ <div>
45
+ <label className="label uppercase color-text-primary">{row.label}</label>
46
+ {row.getContent()}
47
+ </div>
48
+ );
49
+ };
50
+
51
+ const MetricResultStats = ({ metricConfig, metricSetPair, run }: IMetricResultStatsStateProps) => {
52
+ const tableColumns: Array<ITableColumn<string>> = [
53
+ {
54
+ getContent: (target) => <span>{target === 'control' ? 'Baseline' : 'Canary'}</span>,
55
+ },
56
+ {
57
+ label: 'start',
58
+ getContent: (target) => <FormattedDate dateIso={metricSetPair.scopes[target].startTimeIso} />,
59
+ hide: () => {
60
+ const request = run.canaryExecutionRequest;
61
+ const configuredControlStart = request.scopes[metricConfig.scopeName].controlScope.start;
62
+ const actualControlStart = metricSetPair.scopes.control.startTimeIso;
63
+
64
+ const configuredExperimentStart = request.scopes[metricConfig.scopeName].experimentScope.start;
65
+ const actualExperimentStart = metricSetPair.scopes.experiment.startTimeIso;
66
+
67
+ return configuredControlStart === actualControlStart && configuredExperimentStart === actualExperimentStart;
68
+ },
69
+ },
70
+ {
71
+ label: 'count',
72
+ getContent: (target) => <span>{getStats(run, metricSetPair.id, target).count}</span>,
73
+ },
74
+ {
75
+ label: 'avg',
76
+ getContent: (target) => <span>{round(getStats(run, metricSetPair.id, target).mean, 3)}</span>,
77
+ },
78
+ {
79
+ label: 'max',
80
+ getContent: (target) => <span>{round(getStats(run, metricSetPair.id, target).max, 3)}</span>,
81
+ },
82
+ {
83
+ label: 'min',
84
+ getContent: (target) => <span>{round(getStats(run, metricSetPair.id, target).min, 3)}</span>,
85
+ },
86
+ ];
87
+
88
+ const metadataRows: IResultMetadataRow[] = [
89
+ {
90
+ label: 'classification reason',
91
+ getContent: () => {
92
+ const result = run.result.judgeResult.results.find((r) => r.id === metricSetPair.id);
93
+
94
+ if (!result.classificationReason) {
95
+ return null;
96
+ }
97
+
98
+ return <p>{result.classificationReason}</p>;
99
+ },
100
+ },
101
+ ];
102
+
103
+ return (
104
+ <div className="metric-stats vertical">
105
+ {metadataRows.map((row) => (
106
+ <ResultMetadataRow row={row} key={row.label} />
107
+ ))}
108
+ <NativeTable columns={tableColumns} rows={['control', 'experiment']} rowKey={(row) => row} />
109
+ </div>
110
+ );
111
+ };
112
+
113
+ const mapStateToProps = (state: ICanaryState): IMetricResultStatsStateProps => ({
114
+ metricConfig: selectedMetricConfigSelector(state),
115
+ metricSetPair: state.selectedRun.metricSetPair.pair,
116
+ run: runSelector(state),
117
+ service: selectedMetricConfigSelector(state).query.type,
118
+ });
119
+
120
+ export default connect(mapStateToProps)(MetricResultStats);
@@ -0,0 +1,12 @@
1
+ .metric-results {
2
+ &:after {
3
+ content: '';
4
+ position: absolute;
5
+ bottom: 0;
6
+ left: 0;
7
+ right: 0;
8
+ height: 8px;
9
+ box-shadow: inset 0px -8px 4px -4px #fff;
10
+ z-index: 2;
11
+ }
12
+ }
@@ -0,0 +1,52 @@
1
+ import { ICanaryAnalysisResult } from 'kayenta/domain/ICanaryJudgeResult';
2
+ import { ICanaryState } from 'kayenta/reducers';
3
+ import { judgeResultSelector } from 'kayenta/selectors';
4
+ import * as React from 'react';
5
+ import { connect } from 'react-redux';
6
+
7
+ import ListDetail from '../../layout/listDetail';
8
+ import MetricResultDetail from './metricResultDetail';
9
+ import MetricResultsList from './metricResultsList';
10
+
11
+ import './metricResults.less';
12
+
13
+ interface IMetricResultsStateProps {
14
+ metricResults: ICanaryAnalysisResult[];
15
+ selectedMetricResult: ICanaryAnalysisResult;
16
+ }
17
+
18
+ const MetricResults = ({ metricResults, selectedMetricResult }: IMetricResultsStateProps) => {
19
+ const list = <MetricResultsList results={metricResults} />;
20
+ const detail = <MetricResultDetail result={selectedMetricResult} />;
21
+
22
+ return (
23
+ <ListDetail
24
+ list={list}
25
+ listClass="vertical"
26
+ listWidth={5}
27
+ detail={detail}
28
+ detailClass="vertical"
29
+ detailWidth={9}
30
+ className="metric-results flex-1"
31
+ />
32
+ );
33
+ };
34
+
35
+ const mapStateToProps = (state: ICanaryState): IMetricResultsStateProps => {
36
+ const {
37
+ selectedRun: { selectedGroup, selectedMetric },
38
+ } = state;
39
+ const result = judgeResultSelector(state);
40
+
41
+ // Build list of metric results to render.
42
+ const filter: (r: ICanaryAnalysisResult) => boolean = selectedGroup
43
+ ? (r) => r.groups.includes(selectedGroup)
44
+ : () => true;
45
+
46
+ return {
47
+ metricResults: Object.values(result.results).filter(filter),
48
+ selectedMetricResult: Object.values(result.results).find((r) => r.id === selectedMetric),
49
+ };
50
+ };
51
+
52
+ export default connect(mapStateToProps)(MetricResults);