@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,90 @@
1
+ import * as Creators from 'kayenta/actions/creators';
2
+ import { CanarySettings } from 'kayenta/canary.settings';
3
+ import { DISABLE_EDIT_CONFIG, DisableableInput, DisableableTextarea } from 'kayenta/layout/disableable';
4
+ import FormList from 'kayenta/layout/formList';
5
+ import FormRow from 'kayenta/layout/formRow';
6
+ import { ICanaryState } from 'kayenta/reducers';
7
+ import * as React from 'react';
8
+ import { connect } from 'react-redux';
9
+ import { Action } from 'redux';
10
+
11
+ import MetricStoreSelector from './metricStoreSelector';
12
+
13
+ interface INameAndDescriptionDispatchProps {
14
+ changeName: (event: React.ChangeEvent<HTMLInputElement>) => void;
15
+ changeDescription: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
16
+ }
17
+
18
+ interface INameAndDescriptionStateProps {
19
+ name: string;
20
+ description: string;
21
+ }
22
+
23
+ /*
24
+ * Configures canary config name and description.
25
+ */
26
+ function NameAndDescription({
27
+ name,
28
+ description,
29
+ changeName,
30
+ changeDescription,
31
+ }: INameAndDescriptionDispatchProps & INameAndDescriptionStateProps) {
32
+ return (
33
+ <FormList>
34
+ <FormRow label="Configuration Name" inputOnly={true}>
35
+ <DisableableInput
36
+ type="text"
37
+ value={name}
38
+ onChange={changeName}
39
+ disabled={CanarySettings.disableConfigEdit}
40
+ disabledStateKeys={[DISABLE_EDIT_CONFIG]}
41
+ />
42
+ </FormRow>
43
+ <MetricStoreSelector />
44
+ <FormRow label="Description" inputOnly={true}>
45
+ <DisableableTextarea
46
+ className="form-control input-sm"
47
+ value={description}
48
+ onChange={changeDescription}
49
+ disabled={CanarySettings.disableConfigEdit}
50
+ disabledStateKeys={[DISABLE_EDIT_CONFIG]}
51
+ />
52
+ </FormRow>
53
+ </FormList>
54
+ );
55
+ }
56
+
57
+ function mapStateToProps(state: ICanaryState): INameAndDescriptionStateProps {
58
+ if (state.selectedConfig.config) {
59
+ return {
60
+ name: state.selectedConfig.config.name,
61
+ description: state.selectedConfig.config.description,
62
+ };
63
+ } else {
64
+ return {
65
+ name: '',
66
+ description: '',
67
+ };
68
+ }
69
+ }
70
+
71
+ function mapDispatchToProps(dispatch: (action: Action & any) => void): INameAndDescriptionDispatchProps {
72
+ return {
73
+ changeName: (event: React.ChangeEvent<HTMLInputElement>) => {
74
+ dispatch(
75
+ Creators.updateConfigName({
76
+ name: event.target.value,
77
+ }),
78
+ );
79
+ },
80
+ changeDescription: (event: React.ChangeEvent<HTMLTextAreaElement>) => {
81
+ dispatch(
82
+ Creators.updateConfigDescription({
83
+ description: event.target.value,
84
+ }),
85
+ );
86
+ },
87
+ };
88
+ }
89
+
90
+ export default connect(mapStateToProps, mapDispatchToProps)(NameAndDescription);
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { connect } from 'react-redux';
3
+ import { Action } from 'redux';
4
+
5
+ import * as Creators from '../actions/creators';
6
+ import ConfigJsonModal from './configJsonModal';
7
+
8
+ interface IOpenConfigJsonModalDispatchProps {
9
+ openConfigJsonModal: () => void;
10
+ }
11
+
12
+ /*
13
+ * Button for opening the edit config JSON modal.
14
+ */
15
+ function OpenConfigJsonModalButton({ openConfigJsonModal }: IOpenConfigJsonModalDispatchProps) {
16
+ return (
17
+ <div>
18
+ <button className="passive" onClick={openConfigJsonModal}>
19
+ <i className="far fa-file-code" />
20
+ <span>JSON</span>
21
+ </button>
22
+ <ConfigJsonModal />
23
+ </div>
24
+ );
25
+ }
26
+
27
+ function mapDispatchToProps(dispatch: (action: Action & any) => void): IOpenConfigJsonModalDispatchProps {
28
+ return {
29
+ openConfigJsonModal: () => dispatch(Creators.openConfigJsonModal()),
30
+ };
31
+ }
32
+
33
+ export default connect(null, mapDispatchToProps)(OpenConfigJsonModalButton);
@@ -0,0 +1,50 @@
1
+ import * as Creators from 'kayenta/actions/creators';
2
+ import { CanarySettings } from 'kayenta/canary.settings';
3
+ import { DISABLE_EDIT_CONFIG, DisableableButton } from 'kayenta/layout/disableable';
4
+ import { ICanaryState } from 'kayenta/reducers';
5
+ import * as React from 'react';
6
+ import { connect } from 'react-redux';
7
+
8
+ import DeleteConfigModal from './deleteModal';
9
+
10
+ interface IDeleteButtonStateProps {
11
+ disabled: boolean;
12
+ }
13
+
14
+ interface IDeleteButtonDispatchProps {
15
+ openDeleteConfigModal: () => void;
16
+ }
17
+
18
+ /*
19
+ * Button for opening the delete canary config confirmation modal.
20
+ */
21
+ function DeleteConfigButton({ openDeleteConfigModal, disabled }: IDeleteButtonDispatchProps & IDeleteButtonStateProps) {
22
+ return (
23
+ <div>
24
+ <DisableableButton
25
+ className="passive"
26
+ disabled={disabled || CanarySettings.disableConfigEdit}
27
+ onClick={openDeleteConfigModal}
28
+ disabledStateKeys={[DISABLE_EDIT_CONFIG]}
29
+ >
30
+ <i className="fa fa-trash" />
31
+ <span>Delete</span>
32
+ </DisableableButton>
33
+ <DeleteConfigModal />
34
+ </div>
35
+ );
36
+ }
37
+
38
+ function mapStateToProps(state: ICanaryState) {
39
+ return {
40
+ disabled: (state.selectedConfig.config && state.selectedConfig.config.isNew) || CanarySettings.disableConfigEdit,
41
+ };
42
+ }
43
+
44
+ function mapDispatchToProps(dispatch: any): IDeleteButtonDispatchProps {
45
+ return {
46
+ openDeleteConfigModal: () => dispatch(Creators.openDeleteConfigModal()),
47
+ };
48
+ }
49
+
50
+ export default connect(mapStateToProps, mapDispatchToProps)(DeleteConfigButton);
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+
3
+ import { HelpField } from '@spinnaker/core';
4
+
5
+ interface IOwnedByProps {
6
+ owningApplications: string[];
7
+ currentApplication: string;
8
+ }
9
+
10
+ const buildHelpText = (ownedBy: string[]): string => {
11
+ if (ownedBy.length === 0) {
12
+ return null;
13
+ } else if (ownedBy.length === 1) {
14
+ return `This config can only be edited from within the application <strong>${ownedBy[0]}.</strong>`;
15
+ } else {
16
+ return `This config can only be edited from within the following applications: <strong>${ownedBy.join(
17
+ ', ',
18
+ )}.</strong>`;
19
+ }
20
+ };
21
+
22
+ export const OwnedBy = ({ owningApplications, currentApplication }: IOwnedByProps) => {
23
+ if (owningApplications.length < 2 && owningApplications.includes(currentApplication)) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <span>
29
+ Owned by: {owningApplications.join(', ')}
30
+ &nbsp;
31
+ <HelpField placement="right" content={buildHelpText(owningApplications)} />
32
+ </span>
33
+ );
34
+ };
@@ -0,0 +1,19 @@
1
+ import classNames from 'classnames';
2
+ import * as React from 'react';
3
+
4
+ import SaveConfigButton from './saveConfigButton';
5
+ import SaveConfigError from './saveConfigError';
6
+ import ValidationErrors from './validationErrors';
7
+
8
+ /*
9
+ * Responsible for canary config save component layout.
10
+ */
11
+ export default function Save() {
12
+ return (
13
+ <div className={classNames('col-sm-12', 'text-right')}>
14
+ <ValidationErrors />
15
+ <SaveConfigError />
16
+ <SaveConfigButton />
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,65 @@
1
+ import { CanarySettings } from 'kayenta/canary.settings';
2
+ import * as React from 'react';
3
+ import { connect } from 'react-redux';
4
+
5
+ import { SubmitButton } from '@spinnaker/core';
6
+
7
+ import * as Creators from '../actions/creators';
8
+ import { ICanaryState } from '../reducers';
9
+ import { AsyncRequestState } from '../reducers/asyncRequest';
10
+
11
+ interface ISaveButtonStateProps {
12
+ saveConfigState: AsyncRequestState;
13
+ inSyncWithServer: boolean;
14
+ disable: boolean;
15
+ }
16
+
17
+ interface ISaveButtonDispatchProps {
18
+ saveConfig: () => void;
19
+ }
20
+
21
+ /*
22
+ * Button for saving a canary config.
23
+ */
24
+ function SaveConfigButton({
25
+ saveConfigState,
26
+ inSyncWithServer,
27
+ saveConfig,
28
+ disable,
29
+ }: ISaveButtonStateProps & ISaveButtonDispatchProps) {
30
+ if (inSyncWithServer && saveConfigState !== AsyncRequestState.Requesting) {
31
+ return (
32
+ <span className="btn btn-link disabled">
33
+ <i className="far fa-check-circle" /> In sync with server
34
+ </span>
35
+ );
36
+ } else {
37
+ return (
38
+ <SubmitButton
39
+ label="Save Changes"
40
+ onClick={saveConfig}
41
+ isDisabled={disable || CanarySettings.disableConfigEdit}
42
+ submitting={saveConfigState === AsyncRequestState.Requesting}
43
+ />
44
+ );
45
+ }
46
+ }
47
+
48
+ function mapStateToProps(state: ICanaryState): ISaveButtonStateProps {
49
+ const disable = !!state.selectedConfig.validationErrors.length || CanarySettings.disableConfigEdit;
50
+ return {
51
+ saveConfigState: state.selectedConfig.save.state,
52
+ inSyncWithServer: state.selectedConfig.isInSyncWithServer,
53
+ disable,
54
+ };
55
+ }
56
+
57
+ function mapDispatchToProps(dispatch: any): ISaveButtonDispatchProps {
58
+ return {
59
+ saveConfig: () => {
60
+ dispatch(Creators.saveConfig());
61
+ },
62
+ };
63
+ }
64
+
65
+ export default connect(mapStateToProps, mapDispatchToProps)(SaveConfigButton);
@@ -0,0 +1,59 @@
1
+ import classNames from 'classnames';
2
+ import * as React from 'react';
3
+ import { connect } from 'react-redux';
4
+
5
+ import * as Creators from '../actions/creators';
6
+ import { ICanaryState } from '../reducers';
7
+ import { AsyncRequestState } from '../reducers/asyncRequest';
8
+
9
+ interface ISaveErrorStateProps {
10
+ saveConfigState: AsyncRequestState;
11
+ saveConfigErrorMessage: string;
12
+ }
13
+
14
+ interface ISaveErrorDispatchProps {
15
+ dismissError: () => void;
16
+ }
17
+
18
+ /*
19
+ * Renders canary config save error.
20
+ */
21
+ function SaveConfigError({
22
+ saveConfigState,
23
+ saveConfigErrorMessage,
24
+ dismissError,
25
+ }: ISaveErrorStateProps & ISaveErrorDispatchProps) {
26
+ return (
27
+ saveConfigState === AsyncRequestState.Failed && (
28
+ <span className={classNames('alert', 'alert-danger')}>
29
+ {buildErrorMessage(saveConfigErrorMessage)}
30
+ <a className={classNames('alert-dismiss', 'clickable')} onClick={dismissError}>
31
+ {' '}
32
+ [dismiss]
33
+ </a>
34
+ </span>
35
+ )
36
+ );
37
+ }
38
+
39
+ function buildErrorMessage(saveConfigErrorMessage: string): string {
40
+ const message = 'There was an error saving your config';
41
+ return saveConfigErrorMessage ? message + `: ${saveConfigErrorMessage}.` : message + '.';
42
+ }
43
+
44
+ function mapStateToProps(state: ICanaryState): ISaveErrorStateProps {
45
+ return {
46
+ saveConfigState: state.selectedConfig.save.state,
47
+ saveConfigErrorMessage: state.selectedConfig.save.error,
48
+ };
49
+ }
50
+
51
+ function mapDispatchToProps(dispatch: any): ISaveErrorDispatchProps {
52
+ return {
53
+ dismissError: () => {
54
+ dispatch(Creators.dismissSaveConfigError());
55
+ },
56
+ };
57
+ }
58
+
59
+ export default connect(mapStateToProps, mapDispatchToProps)(SaveConfigError);
@@ -0,0 +1,35 @@
1
+ import TitledSubsection from 'kayenta/layout/titledSubsection';
2
+ import * as React from 'react';
3
+ import { connect } from 'react-redux';
4
+
5
+ import GroupWeights from './groupWeights';
6
+ import JudgeSelect, { JudgeSelectRenderState } from './judgeSelect';
7
+ import FormList from '../layout/formList';
8
+ import { ICanaryState } from '../reducers/index';
9
+
10
+ interface IScoringStateProps {
11
+ renderJudgeSelect: boolean;
12
+ }
13
+
14
+ function Scoring({ renderJudgeSelect }: IScoringStateProps) {
15
+ return (
16
+ <FormList>
17
+ {renderJudgeSelect && (
18
+ <TitledSubsection title="Judge">
19
+ <JudgeSelect />
20
+ </TitledSubsection>
21
+ )}
22
+ <TitledSubsection title="Metric Group Weights" helpKey="canary.config.metricGroupWeights">
23
+ <GroupWeights />
24
+ </TitledSubsection>
25
+ </FormList>
26
+ );
27
+ }
28
+
29
+ function mapStateToProps(state: ICanaryState) {
30
+ return {
31
+ renderJudgeSelect: state.selectedConfig.judge.renderState !== JudgeSelectRenderState.None,
32
+ };
33
+ }
34
+
35
+ export default connect(mapStateToProps)(Scoring);
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import CenteredDetail from '../layout/centeredDetail';
3
+
4
+ export default function SelectConfig() {
5
+ return (
6
+ <CenteredDetail>
7
+ <h3 className="heading-3">Click a configuration to edit.</h3>
8
+ </CenteredDetail>
9
+ );
10
+ }
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { connect } from 'react-redux';
3
+ import { HoverablePopover } from '@spinnaker/core';
4
+ import { ICanaryState } from '../reducers/index';
5
+
6
+ interface IConfigValidationErrorsStateProps {
7
+ errors: string[];
8
+ }
9
+
10
+ const ConfigValidationErrors = ({ errors }: IConfigValidationErrorsStateProps) => {
11
+ if (!errors || !errors.length) {
12
+ return null;
13
+ }
14
+
15
+ const template = (
16
+ <section>
17
+ <p>The following errors may prevent this config from working properly:</p>
18
+ <ul>
19
+ {errors.map((e) => (
20
+ <li key={e}>{e}</li>
21
+ ))}
22
+ </ul>
23
+ </section>
24
+ );
25
+
26
+ return (
27
+ <HoverablePopover placement={'left'} template={template}>
28
+ <button className="btn btn-link">
29
+ <i className="fa fa-exclamation-triangle" />
30
+ </button>
31
+ </HoverablePopover>
32
+ );
33
+ };
34
+
35
+ const mapStateToProps = (state: ICanaryState): IConfigValidationErrorsStateProps => ({
36
+ errors: state.selectedConfig.validationErrors.map((e) => e.message),
37
+ });
38
+
39
+ export default connect(mapStateToProps)(ConfigValidationErrors);
@@ -0,0 +1,6 @@
1
+ export * from './actions';
2
+ export * from './domain';
3
+ export * from './metricStore';
4
+ export * from './selectors';
5
+ export * from './layout';
6
+ export * from './reducers';
@@ -0,0 +1,20 @@
1
+ import classNames from 'classnames';
2
+ import * as React from 'react';
3
+
4
+ import { DISABLE_EDIT_CONFIG, DisableableButton } from './disableable';
5
+
6
+ interface IAddNewButtonProps {
7
+ onClick: () => void;
8
+ className?: string;
9
+ label?: string;
10
+ }
11
+
12
+ export default ({ onClick, className, label }: IAddNewButtonProps) => (
13
+ <DisableableButton
14
+ onClick={onClick}
15
+ disabledStateKeys={[DISABLE_EDIT_CONFIG]}
16
+ className={classNames('add-new', 'btn', 'btn-block', 'btn-sm', className)}
17
+ >
18
+ <span className="glyphicon glyphicon-plus-sign" /> {label || 'Add new'}
19
+ </DisableableButton>
20
+ );
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+
3
+ export interface ICenteredDetailProps {
4
+ children: JSX.Element;
5
+ }
6
+
7
+ export default function CenteredDetail({ children }: ICenteredDetailProps) {
8
+ return (
9
+ <div className="row">
10
+ <div className="col-sm-offset-4">{children}</div>
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+
3
+ export interface IDeleteButtonProps {
4
+ onClick: () => void;
5
+ }
6
+
7
+ export default ({ onClick }: IDeleteButtonProps) => (
8
+ <a className="clickable" onClick={onClick}>
9
+ <span className="glyphicon glyphicon-trash" />
10
+ </a>
11
+ );
@@ -0,0 +1,87 @@
1
+ import classNames from 'classnames';
2
+ import { ICanaryState } from 'kayenta/reducers';
3
+ import { get, omit } from 'lodash';
4
+ import * as React from 'react';
5
+ import { Typeahead, TypeaheadProps } from 'react-bootstrap-typeahead';
6
+ import { connect } from 'react-redux';
7
+ import Select, { ReactSelectProps } from 'react-select';
8
+
9
+ // Well-known keys that flag if a component should be disabled.
10
+ export const DISABLE_EDIT_CONFIG = 'app.disableConfigEdit';
11
+
12
+ interface IDisableable {
13
+ disabled?: boolean;
14
+ }
15
+
16
+ interface IDisableableStateProps {
17
+ disabledBecauseOfState: boolean;
18
+ }
19
+
20
+ interface IDisableableOwnProps {
21
+ disabledStateKeys: string[];
22
+ }
23
+
24
+ const mapStateToProps = (state: ICanaryState, ownProps: IDisableableOwnProps) => ({
25
+ disabledBecauseOfState: (ownProps.disabledStateKeys || []).some((key) => get<boolean>(state, key, false)),
26
+ });
27
+
28
+ // A component wrapped in `disableable` is disabled if one of the keys passed
29
+ // through `disabledStateKeys` returns true when checked against the Redux store.
30
+ function disableable<T extends IDisableable>(Component: React.SFC<T>) {
31
+ return connect(mapStateToProps)((props: T & IDisableable & IDisableableStateProps) => {
32
+ const { disabled, disabledBecauseOfState } = props;
33
+
34
+ // Would use object spread except for weird interaction with TS generics.
35
+ const otherProps: T = omit(props, ['disabled', 'disabledBecauseOfState', 'disabledStateKeys', 'dispatch']);
36
+ return <Component {...otherProps} disabled={disabled || disabledBecauseOfState} />;
37
+ });
38
+ }
39
+
40
+ type IDisableableButtonProps = React.DetailedHTMLProps<
41
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
42
+ HTMLButtonElement
43
+ >;
44
+ export const DisableableButton = disableable<IDisableableButtonProps>((props) => {
45
+ const { children, ...otherProps } = props;
46
+ return <button {...otherProps}>{children}</button>;
47
+ });
48
+
49
+ type IDisableableInputProps = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
50
+ export const DisableableInput = disableable<IDisableableInputProps>((props) => {
51
+ const { className, ...inputProps } = props;
52
+ return (
53
+ <input
54
+ {...inputProps}
55
+ className={classNames(className, {
56
+ 'form-control': !['radio', 'checkbox'].includes(inputProps.type),
57
+ 'input-sm': !['radio', 'checkbox'].includes(inputProps.type),
58
+ })}
59
+ />
60
+ );
61
+ });
62
+
63
+ export const DisableableReactSelect = disableable<ReactSelectProps>((props) => {
64
+ const { children, ...selectProps } = props;
65
+ return <Select {...selectProps}>{children}</Select>;
66
+ });
67
+
68
+ export const DisableableReactTypeahead = disableable<TypeaheadProps<any>>((props) => {
69
+ const { children, ...selectProps } = props;
70
+ return <Typeahead {...selectProps}>{children}</Typeahead>;
71
+ });
72
+
73
+ type IDisableableSelectProps = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>;
74
+ export const DisableableSelect = disableable<IDisableableSelectProps>((props) => {
75
+ const { children, ...selectProps } = props;
76
+ return <select {...selectProps}>{children}</select>;
77
+ });
78
+
79
+ // TODO(dpeach): why do I have to type `rows` explicitly here?
80
+ type IDisableableTextareaProps = React.DetailedHTMLProps<
81
+ React.InputHTMLAttributes<HTMLTextAreaElement>,
82
+ HTMLTextAreaElement
83
+ > & { rows?: number };
84
+ export const DisableableTextarea = disableable<IDisableableTextareaProps>((props) => {
85
+ const { className, ...textareaProps } = props;
86
+ return <textarea className={classNames('form-control', 'input-sm', className)} {...textareaProps} />;
87
+ });
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+
3
+ export interface IFormListProps {
4
+ children: JSX.Element[] | JSX.Element;
5
+ }
6
+
7
+ /*
8
+ * Mostly exists to centralize styles for form components.
9
+ * */
10
+ export default function FormList({ children }: IFormListProps) {
11
+ return (
12
+ <ul className="list-group">
13
+ {React.Children.map(
14
+ children,
15
+ (c) =>
16
+ c && (
17
+ <li className="list-unstyled">
18
+ <form role="form" className="form-horizontal container-fluid">
19
+ <div className="col-md-12">{c}</div>
20
+ </form>
21
+ </li>
22
+ ),
23
+ )}
24
+ </ul>
25
+ );
26
+ }
@@ -0,0 +1,36 @@
1
+ import classNames from 'classnames';
2
+ import * as React from 'react';
3
+
4
+ import { HelpField, ValidationMessage } from '@spinnaker/core';
5
+
6
+ export interface IFormRowProps {
7
+ label?: string | React.ReactFragment;
8
+ helpId?: string;
9
+ children?: any;
10
+ checkbox?: boolean;
11
+ error?: string;
12
+ warning?: string;
13
+ inputOnly?: boolean;
14
+ }
15
+
16
+ export default function FormRow({ label, helpId, children, checkbox, error, warning, inputOnly }: IFormRowProps) {
17
+ const style: any = {};
18
+ if (!inputOnly) {
19
+ style.marginTop = '5px';
20
+ }
21
+ if (checkbox) {
22
+ style.marginBottom = '0';
23
+ }
24
+ return (
25
+ <div className="form-group row">
26
+ <label className="col-sm-2 control-label sm-label-right">
27
+ {label} {helpId && <HelpField id={helpId} />}
28
+ </label>
29
+ <div className={classNames('col-sm-10', { checkbox })} style={style}>
30
+ {children}
31
+ {error && <ValidationMessage type="error" message={error} />}
32
+ {warning && <ValidationMessage type="warning" message={warning} />}
33
+ </div>
34
+ </div>
35
+ );
36
+ }
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ import { timestamp } from '@spinnaker/core';
3
+
4
+ export interface IDateLabelProps {
5
+ dateIso: string;
6
+ }
7
+
8
+ export default function FormattedDate({ dateIso }: IDateLabelProps) {
9
+ if (dateIso) {
10
+ return <span>{timestamp(new Date(dateIso).getTime())}</span>;
11
+ } else {
12
+ return <span>N/A</span>;
13
+ }
14
+ }
@@ -0,0 +1,2 @@
1
+ export { default as FormRow } from './formRow';
2
+ export * from './disableable';